Wednesday, November 5, 2014

Minimalistic Architecture

Here's a recommended minimalistic and minimum layers for a modern day line of business applications, especially the SPA ones

  1. App
  2. RichDomainModel - emphasis on rich
  3. RichDomainModel.Test
  4. RichDomainModelMapping
  5. Dto
  6. UnitTestFriendlyDal - Domain Access Layer, emphasis on unit-test friendly

    1. App

    • Hosts the UI
    • Serves DTOs as JSON to browser using ASP.NET Web API. ASP.NET Web API gets and pushes DTOs to rich domain models
    • Uses DTOs for data bag between UI and domain models
    • For wiring dependencies, LightInject is highly recommended, it's a very capable IoC/DI container. For AOP concerns, LightInject has interception capability
      • References RichDomainModel, Dto, LightInject, NHibernate

      2. RichDomainModel

      • Receives and returns DTOs to App project
      • Sample implementation:

      using Dto;
      using System.Collections.Generic;
      using System.Linq;
      using UnitTestFriendlyDal;
      namespace Domain
          public static partial class ProductionDomain
              public class ProductCategory
                  public int    ProductCategoryId   { get; set; }
                  public string ProductCategoryName { get; set; }
                  public static IEnumerable<ProductionDto.ProductCategory> GetAll(IDomainAccess ds)
                      return ds.Query<ProductCategory>().MakeCacheable().ToList()
                          .Select(x => new ProductionDto.ProductCategory { Id = x.ProductCategoryId, Name = x.ProductCategoryName }); 

      • Has no virtual keyword on domain models' members even though NHibernate need domain models' members to be virtual. Uses Virtuosity.Fody to automatically make the domain models' members virtual
      • Though I mentioned above I love the word The, it's better to use an apt naming convention for the domain models. For AdventureWorks example, the tables HumanResources.Department, HumanResources.Employee, Person.Address, Person.Person and Production.Product tables domain models counterparts are: HumanResourcesDomain.Department, HumanResourcesDomain.Employee, PersonDomain.Address, PersonDomain.Person, ProductionDomain.Product. 
      • Do note that the names with Domain suffix are static partial classes, not namespaces, to see why static partial classes are better than namespace, see the link above. As for the need to use suffix/prefix, we cannot nest a class inside another class if it has the same name as the outer class
      • References Dto and UnitTestFriendlyDal only

      3. RichDomainModel.Test

      • Tests the business logic / behavior of the rich domain models
      • References RichDomainModel, RichDomainModelMapping, NHibernate, UnitTestFriendlyDal

      4. RichDomainModelMapping

      • Maps relational entities to RichDomainModel
      • References RichDomainModel and NHibernate only

      5. Dto - data bag between UI and rich domain model

      6. UnitTestFriendlyDal

      • DAL is not data access layer nor repository. This is just a domain access layer that needed its Linq be mockable. The data access layer / repository is NHibernate itself, no need to abstract NHibernate. Don't pile abstractions after abstractions on top of NHibernate, especially if the ORM has a repository, unit-of-work and data access layer built in
      • This is just a thin layer for ORM. NHibernate's .Query (Linq) is an extension method, extension methods can't be mocked properly, hence this interface is created. Abstracting the ORM is not the goal, it's the testability that we are after. Had NHibernate's .Query is not an extesion method, it can be mocked properly, this layer will not be needed. There's no need to add unnecessary abstractions on top of a capable ORM
      • References NHibernate only

      Here's a sample of an SPA stack that applies the layers above

      Sample Code:

      Multi-tenancy concern

      For multi-tenancy, it's better not to use schema on NHibernate. NHibernate doesn't have the capability yet to do proper multi-tenancy, Hibernate has

      Shoehorning schema on NHibernate's ISessionFactory as a mechanism to do multi-tenancy would entail each tenant to have their own session factory. Disadvantage being, as the second level cache for common reference tables amongst tenants have a copy on each tenant's session factory, the second-level cache can't be shared effectively, or can't be shared at all. So changes on common reference tables on one tenant will not appear on other tenants' second level cache. On the other hand, if we isolate the common reference table to one session factory only, the drawback is we cannot navigate nor join tenant entities to those common reference tables as those entities live in their own session factory

      I'll expound and make a simulation of this on another post

      Database schema is a bit fancy as a multi-tenancy mechanism, especially if the ORM is not yet capable of mapping entities to schema on-the-fly or elegantly

      So for now on NHibernate, it's better to use filters for multi-tenancy concerns

      Naming guideline

      Happy Coding!


      1. In one of your previous post I noticed you were trying DryIoc. It seems now that you prefer LightInject.
        I've always used StructureMap in my projects and I've always liked it. It is quite slow, though.
        I was trying to switch to a faster IoC and I decided to test SimpleInjector. I don't seem to be able to figure out which is the easiest (and of course fastest) to integrate in a Web.Api project.
        SimpleInjector seems a little bit too complicated to understand (compared to StructureMap).
        Could you share some light?


        1. I haven't yet tried SimpleInjector, I might check it out

          DryIoc is the fastest, see benchmark:

          There is just no way to dispose objects with DryIoc yet, or I haven't find it out yet. An example use:

          protected override System.Web.Mvc.IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
          System.Web.Mvc.IController ic = controllerType == null
          ? null
          : (System.Web.Mvc.IController)_container.Resolve(controllerType);

          return ic; // when the controller gets disposed?

          On LightInject, resolving objects are enclosed in a scope, the object's Dispose method gets called when the scope ends. An example:

          protected override System.Web.Mvc.IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
          using (_container.BeginScope())

          System.Web.Mvc.IController ic = controllerType == null
          ? null
          : (System.Web.Mvc.IController)_container.GetInstance(controllerType);

          return ic; // this gets disposed when this scope ends

          See the LightInject's Lifetime section here (bookmark is broken):

          LightInject has interception with a nice performance too, DryIoc has no interception yet. Though HaveBox's interception is the fastest, it seems its container has two kinds of lifetimes only, not flexible

          In terms of difficulty, I find the young containers are easier to use as their APIs are lambda-oriented. Other than that, they just differ in naming things, e.g., DryIoc's Resolve versus LightInject's GetInstance

          As for easier to use in ASP.NET Web API, I just followed Mark Seemann's approach:

          Here's an implementation of that adapted to DryIoc (before I learned of LightInject):

          For sure, you'll just change less than five lines to adapt the above approach to LightInject, SimpleInjector, any IoC/DI for that matter

          I think you can't go wrong with LightInject too. Here's one of Daniel Palme's conclusions:

          "DryIoc, LightInject and SimpleInjector offer a very performance combined with support for advanced scenarios like interception and generic decorators.
          Simple Injector and LightInject also provide extensive documentation and support all important platforms."

      2. great post and its realy informative , thanks so much