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.
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.
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.
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.