-
Notifications
You must be signed in to change notification settings - Fork 14
onion architecture ddd
The reference architecture uses a contrived Notebook application to present a number of concepts and best practices. The use case scenarious for a notebook are widely understood; which is why it was chosen.
This document describes the Notebook domain model, and how it is codified and organized in the Core project of this Onion Architecture implementation.
In the Onion Architecture, the Domain Model is the first citizen. It has primacy and sits at the core of the design. All other aspects of the application are either directly or indirectly dependent upon it. The Application exposes Domain behavior to the outside world and it exists in the next layer outside the domain. This reference architecture combines the Domain and the Application into a single .Net project / DLL named Core. This is reflected in the Core project folder structure and the Core project namespaces.
./src/Core/Domain
./src/Core/Application
CompanyName.Notebook.NoteTaking.Core.Domain
CompanyName.Notebook.NoteTaking.Core.Application
The Core project has no dependencies. It is not dependent on any other projects in the solution. And it is not dependent on any NuGet packages or 3rd-party DLLs. In fact, if project dependencies are introduced at any point in time, that is a clear sign that something has gone terribly wrong and must be corrected.
The Core project file is clean.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
</Project>
In this NoteTaking web service, there are exists the following entities:
- Notes
- Categories
- Subscribers
Notes only exist in the context of Categories. Notes whose Category is not explicitly stated are placed in the default Category. Subscribers can subscribe to Categories in order to be notified when Category events are triggered.
In this design, the Category is the aggregated root and it contains a collection of Notes and a collection of Subscribers. State changes the Category and the entities that it contains is only possible by executing Category behaviors as defined in ICategory. Only the Category aggregated root can manipulate the Note and Subscriber entities
Domain models and their interfaces are defined, here.
The Note and Subscriber entities are extraordinarily simple entities. Uncharacteristically, these entities don't have any behaviors. Their state is set via their constructors and should not change of the life of the object. Technically, both could be made into Value Objects. But the next iteration will likely give these objects added depth.
Category is an aggegrated root. It contains collections of Notes and Subscribers and it has several behaviors for altering its state.
Factory interfaces and their implementations are defined, here, for each domain model. This supports dependency injection and inversion of control. It also supports TDD and general testability.
All factory classes have a Create()
method. Most, also, have a Build()
method. The Create()
methods gathers all of the requisite input and creates a brand new instance of an entity.
ICategory Build(
ICategory category,
INoteFactory noteFactory,
ISubscriberFactory subscriberFactory
);
The Build()
method is typically used when hydrating an aggregated root from the database. It takes the flattened entity and combines with it the dependent services that the entity requires in order to perform its work.
Domain services can either be services that the domain models use in order to perform their work or they can be services that act upon the domain models. In the case of the ICategoryRepository, the service acts upon the Category aggregagted root entity. In the case of the INotificationService, the service is used by the Category aggregated root entity in order to perform its work.
The Onion Application layer exposes functionality in the Domain layer to the outside world. In effect, it defines the Application Server. In this case, there are two Application Services being exposed to the outside world
Application Service | Description |
---|---|
INoteTaker | Defines note taking functionality |
IRegistrar | Defines functionality that allows subscribers to subscribe and unsubscribe |
These services are supported by the Message and Exception types defined in Core.Application.Messages and Core.Application.Exceptions. Messages are passed into and out of the Services while custom application exceptions are thrown when something goes wrong.