Layering of an application's codebase is a widely accepted technique to help reduce complexity and improve code reusability. To achieve layered architecture, ASP.NET Boilerplate follows the principles of Domain Driven Design. In Domain Driven Design there are four fundamental layers:
- Presentation Layer: Provides an interface to the user. Uses the Application Layer to achieve user interactions.
- Application Layer: Mediates between the Presentation and Domain Layers. Orchestrates business objects to perform specific application tasks.
- Domain Layer: Includes business objects and their rules. This is heart of the application.
- Infrastructure Layer: Provides generic technical capabilities that support higher layers. An example of the Infrastructure Layer can be a Repository implementation used to interact with a database through an ORM framework, or an implementation for an email provider to send emails.
There may be additional layers added as necessary. An example being:
- Distributed Services Layer: Used to expose application features to remote clients. There are tools like ASP.NET Web API and WCF that can provide this layer.
These are all common layers of a domain-centric architecture. There may be minor differences based on implementation.
ASP.NET Boilerplate Architecture
Overview of layers and structures are shown below:
|Web||Web API Controllers MVC Controllers OData ASP.NET Core|
|Application||Application Services DTOs DTO Mappers Authorization Session Audit Logging|
|Domain (Core)||Entities Value Objects Repositories Domain Services Unit of Work Domain Events|
|Infrastructure||ORM (EntityFramework NHibernate) DB Migrations Background Jobs|
|Dependency Injection Logging Caching|
Here is a solution with five projects for a simple layered application:
A layer can be implemented as one or more assemblies. It may be good to create more than one assembly for third-party dependencies (like EntityFramework here) for larger projects. Also, there may be bounded contextes where every context has it's own layers.
Domain (Core) Layer
Domain Layer is where all business rules should be implemented.
Entities represent data and operations of the business domain. Generally they are mapped to database tables in practice.
Repositories are collection-like objects and are used to retrieve and persist entities on a data source (database). Domain Layer defines repositories, but does not implement them. They are implemented in the Infrastructure Layer.
Domain Events are used to define domain-specific events as well as to trigger and handle them. Domain services work with entities (and other domain objects) and implement business rules which do not belong within a single entity.
Unit of Work is a design pattern used to manage database connection and transaction, track entity changes and save changes to a data store. It's defined in domain layer, but implemented in infrastructure layer.
This layer should be independent of third-party libraries as much as possible.
Application Layer contains application services those are used by the Presentation Layer. An application service method can receive a DTO (Data Transfer Object) as input, uses this input to perform some specific domain layer operation, and may return another DTO, if needed. It should not receive or return entities. An application service method is generally considered a Unit of Work. User input validation is also implemented in this layer. It's suggested to use a tool for mapping entities to DTOs such as the AutoMapper library. We have also session here to obtain information on current user.
While Domain Layer defines interfaces for repositories, unit of work and other services, Infrastructure Layer implements those interfaces. It implements repositories using ORM tools like NHibernate or EntityFramework. ASP.NET Boilerplate provides base classes to work with these two ORM frameworks. Infrastructure Layer is used to abstract away dependencies on third-party libraries from the other layers. Database Migrations can be used here as well.
Beside database access, we may have abstractions for service providers. For example, we may use a vendor to send SMS messages. We can define an interface in domain or application layer to abstract it from our code. Then we can implement that interface in the infrastructure layer.
Web & Presentation Layers
Web Layer is implemented using ASP.NET MVC, Web API and ASP.NET Core. Two different approaches can be implemented here: Single-Page Applications or Multi-Page Applications. Startup templates support both of them.
In a Multi-Page (Classic) Application (MPA), client makes a request to the server, the server side code (ASP.NET MVC Controllers in general) gets data from the database, and Razor views generate HTML. These generated pages are sent back to the client to display it. Each new page results in a full page refresh. Client then can make additional AJAX requests for better user experience.
SPA and MPA involve completely different architectures. An admin panel is a perfect candidate for a SPA. On the other hand, a blog better fits to the MPA model since it is desirable to have it crawled by search engines. Even though there are tools to help make SPAs visible to search engines, the general approach is as such for the time being.
ASP.NET Boilerplate automatically handles exceptions in server-side and returns an appropriate response to the client.
ASP.NET Boilerplate uses and supports Dependency Injection through the Castle Windsor framework. It also uses Log4Net for logging server-side, however, easily supports swapping in other logging libraries without code change by the help of Castle's abstract logging facility.
ASP.NET Boilerplate leverages some of the best frameworks/libraries in addition to its own classes and systems to provide a great infrastructure to build web applications with an NLayer architecture. It also has templates that can be used to easily create a layered solution as a starting point for your application.