The Clean Architecture
13 August 2012
Interface Adapterns are set of Intefaces by which different Deatial lik DB, Infrastructure, Persistence, CCCs are injected to the application. these interfaces make the all the details plugins.
Over the last several years we’ve seen a whole range of ideas regarding the architecture of systems. These include:
- Hexagonal Architecture (a.k.a. Ports and Adapters) by Alistair Cockburn and adopted by Steve Freeman, and Nat Pryce in their wonderful book Growing Object Oriented Software
- Onion Architecture by Jeffrey Palermo
- Screaming Architecture from a blog of mine last year
- DCI from James Coplien, and Trygve Reenskaug.
- BCE by Ivar Jacobson from his book Object Oriented Software Engineering: A Use-Case Driven Approach
Though these architectures all vary somewhat in their details, they are very similar.
They all have the same objective, which is the separation of concerns.
They all achieve this separation by dividing the software into layers.
Each has at least one layer for business rules, and another for interfaces.
They all achieve this separation by dividing the software into layers.
Each has at least one layer for business rules, and another for interfaces.
Each of these architectures produce systems that are:
- 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. Frameworks, DB, ORM, UI Frameworks, CCCs, Infrastrucutre. All these things are details and replaceable and mock able
- 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 diagram at the top of this article is an attempt at integrating all these architectures into a single actionable idea.
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.
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.
Nothing in an inner circle can know anything at all about something in an outer circle.
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.
Method Call and Passing Parameters
Outer Layer
{
innterlayer.Method(Data Types defined in Inner Layer);
}
Application Service Layer
{
// AS include the ref to the Domain Layer
// Domain Layer know nothing about the AS Layer
// While dealing with Lower Layer, its Data Structures are used
//While dealing with Lower Layers, Data Structures of Upper //layer are not used.
//Call Lower Layer in terms of its own Data Structures.
// Talk to Lower Layer in its own Language or Data Structures.
domainLayer.Method(Entities/VO);
Entities/VO = domainLayer.Method();
}
UI Layer
{
// UI Layer include the ref to the AS Layer
// AS Layer know nothing about the UI Layer
// While dealing with Lower Layer, its Data Structures are used
//While dealing with Lower Layers, Data Structures of Uppper //layer are not used.
//Call Lower Layer in terms of its own Data Structures.
// Talk to Lower Layer in its own Language or Data Structures.
We need Auto Mapper here to map the Data Structures of Layers
We need Auto Mapper here to map the Data Structures of Layers
//Request/Reponse are POCO data structures defined in AS Layer
applicationServiceLayer.Method(Request/ResponseModel);
Request/ResponseModel = applicationServiceLayer.Method();
}
//We need Auto Mapper here to map the Data Structures of Layers
During Read Operations
RequestModel <== automapper <== Entities (in AS Layer)
ViewModel <== automapper <== RequestModel (in UI Layer)
During Write Operations
ViewModel ==> automapper ==> RequestModel (in UI Layer)
RequestModel ==> automapper ==> Entities (in AS Layer)
Entities in Domain Model or Domain Layer
Entities encapsulate Enterprise wide business rules.
An entity can be an object with methods, or it can be a set of data structures and functions.
It doesn’t matter so long as the entities could be used by many different applications in the enterprise.
An entity can be an object with methods, or it can be a set of data structures and functions.
It doesn’t matter so long as the entities could be used by many different applications in the enterprise.
If you don’t have an enterprise, and are just writing a single application, then these entities are the business objects of the application.
They encapsulate the most general and high-level rules. They are the least likely to change when something external changes.
For example, you would not expect these objects to be affected by a change to page navigation, or security. No operational change to any particular application should affect the entity layer.
They encapsulate the most general and high-level rules. They are the least likely to change when something external changes.
For example, you would not expect these objects to be affected by a change to page navigation, or security. No operational change to any particular application should affect the entity layer.
Use Cases in Application Service Layer
The software in this layer contains application specific business rules.
It encapsulates and implements all of the use cases of the system.
These use cases orchestrate the flow of data to and from the entities, and direct those entities to use their enterprise wide business rules to achieve the goals of the use case.
It encapsulates and implements all of the use cases of the system.
These use cases orchestrate the flow of data to and from the entities, and direct those entities to use their enterprise wide business rules to achieve the goals of the use case.
We do not expect changes in this layer to affect the entities.
We also do not expect this layer to be affected by changes to externalities such as the database, the UI, or any of the common frameworks.
This layer is isolated from such concerns.
We also do not expect this layer to be affected by changes to externalities such as the database, the UI, or any of the common frameworks.
This layer is isolated from such concerns.
We do, however, expect that changes to the operation of the application will affect the use-cases and therefore the software in this layer.
If the details of a use-case change, then some code in this layer will certainly be affected.
If the details of a use-case change, then some code in this layer will certainly be affected.
Interface Adapters
Data Adapters/ Auto Mapper Components
Convert Data from type of Data Structure to Others
It is this layer, for example, that will wholly contain the MVC architecture of a GUI. The Presenters, Views, and Controllers all belong in here. The models are likely just data structures that are passed from the controllers to the use cases, and then back from the use cases to the presenters and views.
Similarly, data is converted, in this layer, from the form most convenient for entities and use cases, into the form most convenient for whatever persistence framework is being used. i.e. The Database. No code inward of this circle should know anything at all about the database. If the database is a SQL database, then all the SQL should be restricted to this layer, and in particular to the parts of this layer that have to do with the database.
Also in this layer is any other adapter necessary to convert data from some external form, such as an external service, to the internal form used by the use cases and entities.
Frameworks and Drivers.
The outermost layer is generally composed of frameworks and tools such as the Database, the Web Framework, etc. Generally you don’t write much code in this layer other than glue code that communicates to the next circle inwards.
This layer is where all the details go. The Web is a detail. The database is a detail. We keep these things on the outside where they can do little harm.
Only Four Circles?
No, the circles are schematic. You may find that you need more than just these four. There’s no rule that says you must always have just these four. However, The Dependency Rule always applies. Source code dependencies always point inwards. As you move inwards the level of abstraction increases. The outermost circle is low level concrete detail. As you move inwards the software grows more abstract, and encapsulates higher level policies. The inner most circle is the most general.
Crossing boundaries.
At the lower right of the diagram is an example of how we cross the circle boundaries. It shows the Controllers and Presenters communicating with the Use Cases in the next layer. Note the flow of control. It begins in the controller, moves through the use case, and then winds up executing in the presenter. Note also the source code dependencies. Each one of them points inwards towards the use cases.
We usually resolve this apparent contradiction by using the Dependency Inversion Principle. In a language like Java, for example, we would arrange interfaces and inheritance relationships such that the source code dependencies oppose the flow of control at just the right points across the boundary.
For example, consider that the use case needs to call the presenter. However, this call must not be direct because that would violate The Dependency Rule: No name in an outer circle can be mentioned by an inner circle. So we have the use case call an interface (Shown here as Use Case Output Port) in the inner circle, and have the presenter in the outer circle implement it.
The same technique is used to cross all the boundaries in the architectures. We take advantage of dynamic polymorphism to create source code dependencies that oppose the flow of control so that we can conform to The Dependency Rule no matter what direction the flow of control is going in.
What data crosses the boundaries.
Typically the data that crosses the boundaries is simple data structures. You can use basic structs or simple Data Transfer objects if you like. Or the data can simply be arguments in function calls. Or you can pack it into a hashmap, or construct it into an object. The important thing is that isolated, simple, data structures are passed across the boundaries. We don’t want to cheat and pass Entities or Database rows. We don’t want the data structures to have any kind of dependency that violates The Dependency Rule.
For example, many database frameworks return a convenient data format in response to a query. We might call this a RowStructure. We don’t want to pass that row structure inwards across a boundary. That would violate The Dependency Rulebecause it would force an inner circle to know something about an outer circle.
So when we pass data across a boundary, it is always in the form that is most convenient for the inner circle.
Conclusion
Conforming to these simple rules is not hard, and will save you a lot of headaches going forward. By separating the software into layers, and conforming to The Dependency Rule, you will create a system that is intrinsically testable, with all the benefits that implies. When any of the external parts of the system become obsolete, like the database, or the web framework, you can replace those obsolete elements with a minimum of fuss.
Screaming Architecture
30 September 2011
Imagine that you are looking at the blueprints of a building. This document, prepared by an architect, tells you the plans for the building. What do these plans tell you?
If the plans you are looking at are for a single family residence, then you’ll likely see a front entrance, a foyer leading to a living room and perhaps a dining room. There’ll likely be a kitchen a short distance away, close to the dining room. Perhaps a dinette area next to the kitchen, and probably a family room close to that. As you looked at those plans, there’d be no question that you were looking at a house. The architecture would scream: house.
Or if you were looking at the architecture of a library, you’d likely see a grand entrance, an area for check-in-out clerks, reading areas, small conference rooms, and gallery after gallery capable of holding bookshelves for all the books in the library. That architecture would scream: Library.
So what does the architecture of your application scream? When you look at the top level directory structure, and the source files in the highest level package; do they scream: Health Care System, or Accounting System, or Inventory Management System? Or do they scream: Rails, or Spring/Hibernate, or ASP?
The Theme of an Architecture
Go back and read Ivar Jacobson’s seminal work on software architecture: Object Oriented Software Engineering. Notice the subtitle of the book: A use case driven approach. In this book Ivar makes the point that software architectures are structures that support the use cases of the system. Just as the plans for a house or a library scream about the use cases of those buildings, so should the architecture of a software application scream about the use cases of the application.
Architectures are not (or should not) be about frameworks. Architectures should not be supplied by frameworks. Frameworks are tools to be used, not architectures to be conformed to. If your architecture is based on frameworks, then it cannot be based on your use cases.
The Purpose of an Architecture
The reason that good architectures are centered around use-cases is so that architects can safely describe the structures that support those use-cases without committing to frameworks, tools, and environment. Again, consider the plans for a house. The first concern of the architect is to make sure that the house is usable, it is not to ensure that the house is made of bricks. Indeed, the architect takes pains to ensure that the homeowner can decide about bricks, stone, or cedar later, after the plans ensure that the use cases are met.
A good software architecture allows decisions about frameworks, databases, web-servers, and other environmental issues and tools, to be deferred and delayed. A good architecture makes it unnecessary to decide on Rails, or Spring, or Hibernate, or Tomcat or MySql, until much later in the project. A good architecture makes it easy to change your mind about those decisions too. A good architecture emphasizes the use-cases and decouples them from peripheral concerns.
But what about the Web?
Is the web an architecture? Does the fact that your system is delivered on the web dictate the architecture of your system? Of course not! The Web is a delivery mechanism, and your application architecture should treat it as such. The fact that your application is delivered over the web is a detail and should not dominate your system structure. Indeed, the fact that your application is delivered over the web is something you should defer. Your system architecture should be as ignorant as possible about how it is to be delivered. You should be able to deliver it as a console app, or a web app, or a thick client app, or even a web service app, without undue complication or change to the fundamental architecture.
Frameworks are tools, not ways of life.
Frameworks can be very powerful and very useful. Framework authors often believein their frameworks. The examples they write for how to use their frameworks are told from the point of view of a true believer. Other authors who write about the framework also tend to be disciples of the true belief. They show you the way to use the framework. Often it is an all-encompassing, all-pervading, let-the-framework-do-everything position. This is not the position you want to take.
Look at each framework with a jaded eye. View it skeptically. Yes, it might help, but at what cost. How should I use it, and how should I protect myself from it. How can I preserve the use-case emphasis of my architecture? How can I prevent the framework from taking over that architecture.
Testable Architectures.
If you system architecture is all about the use cases, and if you have kept your frameworks at arms-length. Then you should be able to unit-test all those use cases without any of the frameworks in place. You shouldn’t need the web server running in order to run your tests. You shouldn’t need the database connected in order to run your tests. Your business objects should be plain old objects that have no dependencies on frameworks or databases or other complications. Your use case objects should coordinate your business objects. And all of them together should be testable in-situ, without any of the complications of frameworks.
Conclusion
Your architectures should tell readers about the system, not about the frameworks you used in your system. If you are building a health-care system, then when new programmers look at the source repository, their first impression should be: “Oh, this is a heath-care system”. Those new programmers should be able to learn all the use cases of the system, and still not know how the system is delivered. They may come to you and say: “We see some things that look sorta like models, but where are the views and controllers”, and you should say: “Oh, those are details that needn’t concern you at the moment, we’ll show them to you later.”
For more on this topic, see Episode VII - Architecture, Use-cases, and High Level Design, at cleancoders.com.
No comments:
Post a Comment