This is a clean architecture and is a simplified version of onion architecture. It is also conforming to the principals of Hexagonal Architecture.
In the following diagram the arrows show the direction of the dependencies. Any outer layer can directly call ANY inner layer. So the yellow layer (Adapters layer) can rely on all of the other layers. The application layer can only have dependency on the domain layer. And the domain layer should NOT have any references to the other layers. Any communication with the outside word happens in the Adapters layer.
This layer makes it possible for the application to communicate with the outside word. In order to interact with external components like databases, web, files system, etc. the application needs a layer which can convert its internal objects to something which is understandable by the external components and vice versa. For example a Data Access is needed to let the application talk to a database; or to be able to expose the application’s functionality through the Web, we need to create a Web Service which makes use of the .Net Framework web technologies. These components are called Adapters and they should reside in the Adapters layer. The inner layers or other adapters cannot have dependencies on this layer.
As the above diagram shows, there are two groups of external components: driving actors and driven actors (external resources). A Driving actor is an actor that drives the application (take it out of quiescent state to perform one of its advertised functions). End users, automated tests, or remote clients are a few examples of driving actors. Whereas a driven actor is one that the application drives, either to get answers from or to merely notify. Database, files system or remote web services are some examples of driven actors (a.k.a external resources). The distinction between “driving” and “driven” lies in who triggers or is in charge of the conversation. These two groups of the external components require two groups of adapters: Driving adapters and Driven adapters.
These are the adapters that expose the applications functionality to the outside world. In other words it allows the driving actors to drive the application. The other layers of the application never need to see this layer, however the driving adapters need to have dependency on them. The following list encompasses the driving adapters:
- Web Service: Regardless of the web service technology, all of the web services go to this layer. Web API, WCF, or Soap (ASMX). This layer must not have any business or application logic and should be as thin as possible. The consumer of this layer is remote clients.
- Tests: They are another residents of this layer. They call the functions of other layers and allow the test runners drive the application for test purposes. So the test runners are the driving actors of this layer.
- Presentation: This layer can have the adapters that translates the user’s keystrokes and gestures into data understandable for the application and visualize the application’s output to the user. The actor who drives this layer is the end user.
The driving adapters are where the composition root of the entire application should go. Any service registration happens in this layer.
Driven Adapters (Infrastructure)
This layer has the concrete implementation of the dependencies of the other layers. Infrastructure is any code that is a commodity and does not give your application a competitive advantage. This code is most likely to change frequently as the software goes through years of maintenance. That’s why no other layer should have any dependency on this. Any dependency to this layer must be through abstractions. For example if the software needs a repository, it should use an abstract repository (i.e. an interface) and the implementation of the repository goes to this layer. We rely heavily on the Dependency Inversion principle. The application core needs an implementation of core interfaces, and since those implementing classes reside at the edges of the application, we need some mechanism for injecting that code at runtime.
- Data Access: This layer has the implementation of the repositories. They can persist and retrieve the domain objects in some storage. Depending on what storage to use, we may need adapters (different implementation for the repository).
- Service Proxy: Whenever we add a service reference to the application, a proxy component is generated which is an adapter between the application and the remote service. It makes it possible to send and receive the messages throw the web. Depending on the messaging technology, we may need different adapters here. Be it SOAP, TCP or even a message queue, the application should not need to change because of a change in the underlying messaging technology.
- File Access is another example of infrastructure. It makes it possible to operate the file system regardless of the contents of the files.
Note: The adapters in the infrastructure layer should be substituted for test doubles when testing the application.
Application layer (Orchestrator)
This layer contains the application services which are single responsibility classes that do the orchestration of the application and deal with application logic. These services are different from the web services that we have in the Web Service Layer. Please be advised that this layer is not meant to have any domain (a.k.a business) logic. Domain logic should go to the Domain Layer.
- We define the DTO classes (request/response) in this layer.
- Application services are also responsible for composing the response objects.
- As a naming convention suffix them with the word “ApplicationService” or just “Service”. But make it consistent.
- Application services can consume repositories and other domain services.
- Although it does’t see the infrastructure layer, it can see its abstractions.
Domain layer (Business layer)
This layer will have all of the domain models: Entities, values objects, domain events, and the domain exceptions. The abstraction of the repositories also go to this layer. The business rules should be applied in this layer.
Why this architecture
It is a clean architecture that means it can produce a system which is:
- Independent of Frameworks. The architecture does not depend on the existence of some library of feature laden software. This allows you to use such frameworks as tools, rather than having to cram your system into their limited constraints.
- Testable. The business rules can be tested without the UI, Database, Web Server, or any other external element.
- Independent of UI. The UI can change easily, without changing the rest of the system. A Web UI could be replaced with a console UI, for example, without changing the business rules.
- Independent of Database. You can swap out Oracle or SQL Server, for Mongo, BigTable, CouchDB, or something else. Your business rules are not bound to the database.
- Independent of any external agency. In fact your business rules simply don’t know anything at all about the outside world.
The Dependency Rule
The concentric circles represent different areas of software. In general, the further in you go, the higher level the software becomes. The outer circles are mechanisms. The inner circles are policies.
The overriding rule that makes this architecture work is The Dependency Rule. This rule says that source code dependencies can only point inwards. Nothing in an inner circle can know anything at all about something in an outer circle. In particular, the name of something declared in an outer circle must not be mentioned by the code in the an inner circle. That includes, functions, classes. variables, or any other named software entity.
By the same token, data formats used in an outer circle should not be used by an inner circle, especially if those formats are generate by a framework in an outer circle. We don’t want anything in an outer circle to impact the inner circles.
The following tenets are those of the onion architecture. Our architecture should adhere to all of them as it is a simplified version of onion:
- The application is built around an independent object model
- Inner layers define interfaces. Outer layers implement interfaces
- Direction of coupling is toward the centre
- All application core code can be compiled and run separate from infrastructure
Hexagonal Architecture: http://alistair.cockburn.us/Hexagonal+architectur
Onion Architecture: http://jeffreypalermo.com/blog/the-onion-architecture-part-1/
Clean Architecture: http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html