Creating good architecture is as good as preparing a tasty meal with all the necessary ingredients. But when you need to cut those onions, do not shed tears!
Today, we will discuss Onion Architecture which is also said to be a cousin of layered and hexagonal architecture. The web world is a collection of various traditional architectures. Each of these pieces of architecture has its pros and cons.
But precisely what is Onion Architecture, on which principle it is based, what is the essence of Onion Architecture, when to implement it, etc., will be discussed in this article.
Domain Driven Design
Implementing Domain Driven Design (DDD) through onion architecture significantly improves code quality, lowers complexity, and enables the development of evolving business systems.
A domain is a collection of information. It refers to the business knowledge that our programme is attempting to model.
The domain model is at the center of Domain-Driven Design or development, which thoroughly understands a domain’s procedures and regulations. It creates software for complicated requirements by closely connecting the implementation to a changing model of fundamental business ideas.
We now know that Onion Architecture has a significant role in implementing a domain-driven design.
What exactly is onion architecture?
What are the advantages of using onion architecture?
How does it work?
What problems does it solve?
Why is onion architecture so popular among techies?
What is Onion Architecture in Software Development?
Jeffrey Palermo coined the term “Onion Architecture” in 2008. This architecture enables greater application testability, maintainability, and dependability on infrastructures such as databases and services.
The domain entities are the heart and soul of the system. The onion architecture is based on a domain model with layers connected by interfaces.
When domain entities and business rules are at the center of the architecture, the goal is to keep external dependencies as far away from those components as possible, and this brings many advantages:
- It delivers an architecture that is adaptable, sustainable, and portable.
- Layers have a separation of concerns and are not tightly coupled. (We will learn more about separation of concern and couplings as these play a crucial role in it)
- Because all the code depends on deeper layers or the center, it offers superior maintainability.
- Increases the testability of the entire code because unit tests can be written for distinct levels without affecting other modules.
- Changes to frameworks and technologies can be made without influencing the core domain. For example, you can easily replace SQL with MongoDB without affecting the core.
Onion Architecture Principles
The core of an onion architecture comprises several concentric layers that interface with one another. The architecture emphasizes the actual domain models more than the underlying frameworks or technology. It is based on the principle of inversion of control.
The following principles form its foundation —
The circles symbolize distinct levels of responsibility. The deeper we go, the more we know about the domain and business rules. The outer rings are mechanisms (including different switchable modules), whereas the inside circles are fundamental domain logic. The outer layers rely on the inner layers, and the inner layers are unaffected by any changes being introduced in the outer rings.
The outer circle’s classes, methods, variables, and source code generally depend on the inner circle but not the other way around. Data structures and formats might differ between levels. Inner layers shouldn’t use outer layer data formats.
Each layer/circle wraps or conceals internal implementation details while providing an interface to the outer layer. All layers must also supply information that inner layers can easily consume. The objective is to enhance coupling inside a vertical slice across layers while minimizing coupling across layers.
At deeper layers, we define abstract interfaces, while at the top layer, we give their concrete implementation. By doing this, we can keep our attention on the domain model and lessen our concern about implementation issues. We may also use dependency injection frameworks like Spring to link interfaces with implementation at runtime. For Example, Infrastructure layer implementations include external services used in Application Services and repositories used in the domain.
Separation of Concerns
The application is separated into layers, each with its own duties and concerns. Within the application, each layer functions as a module/package/namespace.
Low coupling occurs when one module interacts with another without worrying about the internals of the other module. The internal implementation of external layers does not need to be a concern for all internal levels.
Onion Architecture Layers
The onion architecture employs the concept of layers and heavily relies on the Dependency Inversion Principle. The user interface communicates with business logic using the interfaces and has four layers.
- Domain Entities Layer
- Repository Layer
- Service Layer
- UI (Web/Unit Test) Layer
These layers are closer to the center. The Domain entities in the center represent the business and behavior objects. These layers can change, but the domain entities layer is always in the middle. The other layer describes an object’s behavior in greater detail. Let’s go through each layer one by one.
Domain Entities Layer
The domain layer lies in the heart of the Onion Architecture, representing the business and behavioral objects. All of your domain objects should be located at this core. It stores all of the application domain objects. If an application is built with the ORM entity framework, this layer contains POCO (Plain Old CLR Object) classes (Code First) or Edmx classes (Database First). There are no dependencies between these domain entities. You might also have domain interfaces in addition to domain objects. Additionally, domain objects are flat and free of cumbersome dependencies and code.
The layer is intended to act as an abstraction layer between an application’s Domain Entities layer and its Business Logic layer. We typically include APIs in this layer that offers object saving and retrieval functionality, usually by utilizing a database. A data access pattern encourages a more loosely coupled approach to data access. We create a generic repository that searches the source for data, maps the data from the source to a business entity, and tracks changes in the business entity back to the source.
Interfaces with typical activities such as Add, Save, Edit, and Delete are held in the Service layer. This layer is also used to communicate between the UI and repository layers. It also serves as the business logic layer because it contains business logic for an entity. Service interfaces are maintained distinct from their implementation on this layer to ensure loose coupling and separation of concerns.
It is the outermost layer and contains peripheral aspects such as UI and tests. It represents the Web API or Unit Test project in a Web application. This layer implements the dependency injection principle, allowing the application to design a loosely linked structure and communicate with the internal layer using interfaces.
What are the challenges with traditional architecture, and how onion architecture addressed them?
According to traditional architecture, all the layers are interconnected and significantly dependent on one another. For example, the UI layer communicates with business logic, which communicates with the data layer. There should be a separation of concerns because none of the layers in 3-tier and n-tier structures are independent. Such systems are complicated to comprehend and keep up with. This traditional architecture’s flaw is its needless coupling.
The primary goal of this architecture is to overcome the challenges associated with 3-tier or n-tier architecture and to provide a solution to typical issues such as tight coupling and separation of concerns.
We have already discussed the separation of concerns as one of the principles in Onion Architecture, but we must understand the differences in couplings. There are two kinds of couplings, i.e., Tight Coupling and Loose Coupling.
- Tight Coupling — A class is said to be tightly coupled to another type if it concretely depends on another class. A tightly coupled object depends on another object; that means updating one object in a tightly coupled application, sometimes involving modifications to several other objects. When an application is of lower complexity, making modifications is not challenging, but when it comes to enterprise-level applications, it is too difficult.
- Loose Coupling — It denotes the independence of two objects and the fact that one object can use another without becoming dependent on it. It is a design objective that aims to lessen the interdependencies between system components in order to lower the risk that modifications to one component may necessitate changes to any other component.
These issues were addressed by Onion Architecture, which defined layers from the core to the infrastructure (Separation of Concerns). It follows the fundamental rule by shifting all coupling to the center (Loose Coupling).
Why is onion architecture so popular among techies?
We have already discussed the advantages and challenges this architecture has addressed. These have become the reasons onion architecture has become so popular among the techies.
Technology enthusiasts these days use Model-View-Controller architecture as a preferred web application architecture. It addresses the issue of separation of concerns by separating UI, business logic, and data access logic. The View is responsible for creating the user interface.
The Model is used to move data between the View and the Controller, where the business logic executes any operations. The Controller handles web requests via action methods and returns the appropriate View. As a result, it solves the problem of separation of concerns while still allowing the Controller to perform database access logic.
In essence, MVC resolves the separation of concerns problem, but the tight coupling problem remains.
On the other hand, the Onion Architecture tackles the problems of tight coupling and separation of concerns. The overall principle of the Onion Architecture is to maintain the data access logic, business logic, and Model in the application’s core and move all the dependencies as far outward as practical, implying all connections towards to center.
Although it may initially seem complicated, onion architecture is well-liked in the software development industry. It is also strongly related to two other architectural styles: Layered and Hexagonal. The architecture is robust and makes software evolution simple. The system becomes more accessible to test, manage, and transport by layering the application.
Java developers may not be as interested in Onion Architecture as C# developers. However, the decision to use the architecture is left to the community of architects to debate.
Feel free to get in touch with us if you would like to evaluate the suitability of Onion Architecture for your application!