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!