An example .NET project that utilizes best practices including an IoC container, usage of the Data Transfer Object (DTO) pattern and usage of the Repository/Unit of Work pattern.
Table of Contents
This architecture is the product of learned best practices from the industry at large. This document aims to be a high level overview of these best practices, with references to the original sources where appropriate.
This architecture works best for scenarios where complex business logic needs to be organized and contained for better maintenance by a development team, and can be scaled either horizontally or vertically with ease when paired with the Azure platform.
As an added note, the patterns that are in use here (such as Repository, Unit of Work and Inversion of Control could also be paired with a Microservices architecture to provide finer granularity for scaling. Part of the Roadmap includes adding an example of microservices paired with this architecture.
Acme
|__ Acme.Api
|__ App_Start
|__ Controllers
|__ Infrastructure
|__ Injection
|__ Acme.Core
|__ Domain
|__ DTO
|__ Exceptions
|__ Extensions
|__ Infrastructure
|__ Mapping
|__ Repository
|__ Service
|__ Acme.Data
|__ Configuration
|__ Context
|__ Identity
|__ Infrastructure
|__ Migrations
|__ Repository
The Core project is the heart of the application. Every project depends on Core.
Acme.Core contains all of the interfaces that are necessary for operation of the application. It is also where all of the business logic should be contained inside of services.
AutoMapper 5.1.1 - A convention based object-object mapper. Used for converting Entity objects into Data Transfer Objects.
ASP.Identity.Core - The heart of the ASP Identity system. Used to bind IUser
and IRole
to Entity objects and expose the UserManager
class to manage membership.
-
This is where you store your entity definitions.
Why?: Placing entities in
Core
allows you to write multiple data layers and/or make it easier to swap out your data layer for another.
- This is where you store your Data Transfer Object definitions. (See Data Transfer Object for an explanation of these classes)
- This is where you store your custom application exceptions.
- This is where you store your C# Extension methods.
- This is where the "plumbing" for the Core project resides. The majority of interfaces should reside in this folder.
- This is where your AutoMapper Profiles reside.
- This is where your Entity repository interfaces reside. (See Interface Segregation Principle)
- This is where your Business Logic resides, encapsulated in Service classes.
The Data project is where your data access code resides.
Business logic is banned from this project.
The Data project is solely responsible for data access operations. It provides implementations of the IRepository/IUnitOfWork interfaces found in the Core project.
No business logic allowed!
Why?: If you opt for another database technology (Moving from SQL Server to Postgres/MongoDB etc), you will have to rewrite business logic to work with that particular data access logic.
Keep Business Logic In The Core™!
Entity Framework 6.1.3 - A convention based object-object mapper. Used for converting Entity objects into Data Transfer Objects.
ASP.Identity.Core - The heart of the ASP Identity system. Used to implement the IUserStore
interface for UserManager
to consume in the Core project.
- This is where your Entity Framework Configurations reside.
- This is where your Data Contexts reside. Currently this project has one data context, but you could have multiple data contexts here to support a multi-database application.
- This is where the implementation of
IUserStore
resides.
- This is where the "plumbing" for the Data project resides. Implementations for the
IRepository<TEntity>
andIUnitOfWork
interfaces found inAcme.Core.Infrastructure
reside in this folder.
- This is where your Entity Framework Migrations reside.
- This is where the implementations of your Entity interfaces found in Core reside. See (See Interface Segregation Principle)
The API project is where you expose your application data to your clients and bind all your interfaces to their matching implementations.
Business logic is banned from this project
The API project is solely responsible for transporting Data Transfer Objects to and from your Service Layer and front-end clients.
No business logic allowed!
Why?: If you opt for another REST API technology (Moving from ASP Web API 2 to Nancy, ASP.NET Core etc), you will have to rewrite business logic to work with that particular technology.
Keep Business Logic In The Core™!
AutoMapper 5.1.1 - A convention based object-object mapper. Residual dependency necessary for injecting IMapper
into the Service Layer
Microsoft.Owin.Cors - Enables Cross Origin Resource Sharing for the API.
SimpleInjector - Used as the IoC container for the application.
This is where your OWIN configuration code resides, split among several partial classes. This is done to improve maintainability when your configuration code inevitably grows larger.
This is where your API controllers reside. These should only ever have Services injected into them, never repositories.
Why?: If repositories are injected into your controllers, it's a good indicator that business logic is about to be performed in your controller. Even basic CRUD actions are considered to be business logic, because these actions are operating on entities, which represent your business.
- This is where the "plumbing" for the API project resides. Currently there is a single class in this folder - an implementation of a
Session
class to make the current user available to every project (even all the way down to the data access layer!)
- This is where your IoC configuration resides. Configuration is split among several
Packages
to improve maintability in the inevitable event that more types of services are created. See this stack overflow answer for discussion on this topic.
Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.
http://martinfowler.com/eaaCatalog/repository.html
Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems.
The Inversion of Control (IoC) and Dependency Injection (DI) patterns are all about softening dependencies in your code.
http://joelabrahamsson.com/inversion-of-control-an-introduction-with-examples-in-net/
Defines an application's boundary with a layer of services that establishes a set of available operations and coordinates the application's response in each operation.
Used to store Business Logic
An object that carries data between processes in order to reduce the number of method calls.
http://martinfowler.com/eaaCatalog/dataTransferObject.html
http://stackoverflow.com/a/725365
http://www.aspnetboilerplate.com/Pages/Documents/Data-Transfer-Objects
An object model of the domain that incorporates both behavior and data.
Important Note about the Domain Layer
You'll see a lot of times in this document the saying Keep Business Logic In The Core!
. This does not mean Keep Business Logic in the Service Layer!
.
You can, and you absolutely should write business logic in the Domain model as well as the service layer.
Keeping your domain layer void of any business logic can lead to an Anemic Domain Model as described my Martin Fowler.
The Database Factory is an Entity Framework specific solution for the Stale Context problem. It allows the DataContexts in use in the application to be scoped to the current request lifecycle, while hiding this fact from the rest of the architecture.
Migrations help you maintain the state of a Database schema inside of your application codebase.
http://www.entityframeworktutorial.net/code-first/migration-in-code-first.aspx
The interface-segregation principle (ISP) states that no client should be forced to depend on methods it does not use.[1] ISP splits interfaces that are very large into smaller and more specific ones so that clients will only have to know about the methods that are of interest to them.
https://en.wikipedia.org/wiki/Interface_segregation_principle
- Add an example of Microservices architecture using Azure Surface Fabric.