Tuesday, October 22, 2013

A ubiquitos language for a typical 3-tier .Net Application

Introduction

There many different definitions/terms in a typical 3-tier application that often cause confusions among application developers. Following the philosophy of domain driven design (DDD) (see http://en.wikipedia.org/wiki/Domain-driven_design  and http://dddcommunity.org/learning-ddd/what_is_ddd/) , we try to develop a ubiquitous language (http://martinfowler.com/bliki/UbiquitousLanguage.html ) for a typical 3-tier .Net application architecture. The ubiquitous language facilitates communication among developers and helps to standardized code naming conventions. Here we summarize the best practices used in a typical three-tier .Net application (http://en.wikipedia.org/wiki/Multitier_architecture). It also helps to clarify concepts such as whether or not to define business logic in a domain entity. 

Three-tier Application Architecture

The three tiers are Database tier, Domain tier and View tier. There are three types of data used in a three-tier application: Database Data, Domain Entities and View Models. The operations on these data are called services. Correspondingly, the services are named Database service, Domain Service, and Model Service. In addition to their services, the data tier and view tier also include data mapping functions that map their data to/from domain entities. Domain tier only has domain entities and entity methods that implement domain-specific processing logics.

The following diagram depicts the relationships among different modules in the three tiers. The arrows describe a use or dependent relationship between two modules. The diagram suggests that the domain tier is agnostic of the data tier and the view tier. In Visual Studio, the domain project should not dependent on the view project and, maybe a surprise to a few people, the database project. The runtime dependency of view à domain à database is resolved by an independency inject framework such as Unity.

The texts in the parentheses give suggested naming convention for classes and interfaces. For example, a Person entity will have a class name Person, a persistence service interface IPersonDbService, a persistence service implementation named PersonDbService, a Database-Entity mapper called PersonDbMap, a view model named PersonModel and a Enity-View mapper called PersonModelMap. Some modules are optional for a specific project. For example, if a library such as AutoMapper is used for Entity-View mapping, there are no mapping classes for Enity-View Mapping module.  


Domain Tier

As the core of an application, domain entities are domain concepts/terms defined by POCO (Plain Old CLR Object) classes. For example, a person is defined in a Person POCO class. A POCO class usually has a list of public properties that only have get and set accessors defined. Additionally, a domain entity can have single-entity business rules encapsulated in it (see http://martinfowler.com/eaaCatalog/domainModel.html  and http://martinfowler.com/bliki/AnemicDomainModel.html). For example, a Person class has a DateOfBirth property. It can also define a property such as IsAdult to return true/false based on the business domain rule. The property and method definitions of domain entities are solely based on domain-specific requirements.
Cross-entity domain logics should be defined in the domain services. The relationships among domain entities reflect the relationships defined by the ubiquitous language of that domain. The domain service only defines entity aggregations and cross-entity business logics using domain entities and Database service interfaces.  The database tier API is defined in the domain tier with a naming convention of IEntityDbService. For example, the Person entity should have a database service interface IPersonDbService. The interface IPersonDbService is defined in Domain tier because its APIs are derived from domain-specific persistency requirements.

Database Tier 

Database tier provides functions to access persistent SQL or NoSQL database data. Database tier implements the mapping between the persistent data and the domain entities. The mappings are defined in mapping classes that should have a “Map” postfix. For example, the mapping class for the Person entity is named as PersonMap.  All maps are defined in EntityMaps folder.

A database tier service implements IEntityService (defined in Domain tier) that provide CRUD operations on domain entities. Generally there are one interface and one implementation for each entity aggregation. The interface and class should have a “DbService” postfix. For example, a database service implements PersonDbService  for the Person entity’s IPersonDbService interface.

.Net Entity Framework (EF) significantly reduces the workload in data service development. For the mapping task, in code-first development, EF allows us to define a set of data type mapping classes using fluent API (http://msdn.microsoft.com/en-us/data/jj591617.aspx) to map the CLR types (properties) to database tables (columns). The mapping classes also define some constraints such as not null or referential integrity.  

Presentation Tier

View Models are view-specific data classes used by the view layer. It may aggregate multiple domain entities and transform some domain entity properties. For example, we often display related domain entities together and may want to display the date of birth in a user-friendly format.

The view tier provides functions mapping between domain entities and view models. It implements view logics and should only expose high-level simple API to other view layer components (usually controllers in a MVC application). Libraries such as AutoMapper (https://github.com/AutoMapper/AutoMapper) can help to map/transform domain entities to/from view models.

The model classes in view tier should have a postfix of “Model”. For example, the Person class used in a view in the presentation layer is named as PersonModel. If mapper classes are used to map between entities and models, they should have a “ModelMap” postfix. For example, for the Person entity, the view tier mapper is called PersonModelMap.

Project Layout

We put the three tiers into their corresponding projects. In Database project, we have a Mappers folder for data mapping classes and a Services folder for data services. In domain project, we have an Entities folder, a Service folder and a DbInterface folder. In presentation project, we have a Models folder, a Mappers folder, and a Services folder in addition to other common folders such as Views and Controllers.