The Practical Onion Architecture — the onion that doesn’t make you cry

2o5P...cmGf
12 Oct 2022
79

Hi, folks in my new job I’m the owner of implementing a new architecture, so, I was presenting a new architecture, separating the responsibilities between the domains, and services. Also, I propose a new code design based on the Onion Architecture, and I’ll show you in this medium story.

The Onion Architecture

The onion architecture was an architecture proposed by Jeffrey Palermo
The architecture has the purpose that to protect the use case from external interactions. In other words, this approach avoids coupled with external layers, through dependency injection.



Alistair Cockburn has written a bit about Hexagonal architecture. Hexagonal architecture and Onion Architecture share the following premise: Externalize infrastructure and write adapter code so that the infrastructure does not become tightly coupled.




Motivation to use

There are many projects that put the business rule in controller, view, also in data access layers, and sometimes queries with business rules that access the database, creating a strong coupling between the layers, sometimes getting harder the reuse of the classes generating code duplications, getting harder unit tests and others problems.


Hands-on

This hands-on is in Java language with SpringBoot framework but feel free to choose the language and framework that you love.
I want to do this hands-on closer to a real project, so we need context.


Context

The implementation is based on a project in which store cryptocurrencies’ purchase orders



Architecture Overview


IMG — 03 Image based on the Onion Architecture for our project




Firstly generate the modules and project on SpringIO Website, and configure the project as the image below.

Layers such as modules


Main pom.xml




Each maven module represents the layers that were presented in IMG — 03
But you could organize your project using folders. I believe that the layers can be represented as modules, it is easier to maintain, and also more scalable and extensible



Business Layer

The business layer is the system’s core, in charge of keeping the business rules and domain objects.

Use Case: Purchase Order

So, let’s create a new use case, that is in charge to buy a coin. Before buying a coin it’s necessary to check the current value of the coin requested. So we need to request this current value to an external service, in which we gonna use the CoinMarketCap
Knowing these requirements, we can implement the use case with a focus on business rules.

Business Module




In the business module there are the interfaces and classes:
PurchaseOrder — is the domain object of the business layer. Represents a coin purchase order
CreatePurchaseOrder —it’s the use case itself implemented
PurcaseOrderRepository — Interface that communicates with the data-provider layer
CoinIntegration — Interface that communicates with the third-party-service layer



With this approach, we can see that not been necessary yet, to know how will be the implementation of the interfaces.
So, we can implement the use case, just focusing on the business rules, no need to worry, about how will be the database model, or how will be integrated with external services at this moment.
Also, this approach makes it easier, to create unit tests.
Let’s create a unit test for our use case




Now, let’s create the implementation


In this example, we can see that my Business Module doesn't have a framework coupled.

Someday Martin Fowler’s Twitter said:

A chosen framework is stuff that is hard to change. My motivation that not adding spring dependency for the business module is just to show that is possible that the business layer to be without any frameworks.


Data Provider Layer


This layer is in charge of communicating with the store, which saves the purchase order.
There is in this layer the implementation of the PurchaseOrderRepository, so, that layer depends on the business layer and other libs/frameworks that will use for implementation.

pom.xml — data-provider module


Data Provider module — v1



PurchaseOrderRepository — Interface that gonna create the communication between the business and data-provider layers by DI — Dependency Injection.
PurchaseOrderEntity — Represents the database model
PurchaseOrderRepositoryInMemory — implements the interface PurchaseOrderRepository from the business layer, this implementation gonna saves the data in memory.
PurchaseOrderConverter — Convert the model object to the database model


IMG-04 — PurchaseOrderRepositoryInMemory implements the PurchaseOrderRepository



Let’s create a new Integration Test to check if everything is ok with our implementation.





We can see that the business and database layers don’t are coupled … But the implementation of the PurchaseOrderRepositoryInMemory is in charge of for decouple AND saving the purchase’s data.
In this case, the implementation isn’t extensible.
So, let’s improve this

IMG-05 Data Provider module — v1



PurchaseOrderRepositoryImpl — Now, there is an implementation that receives by dependency injection, another interface that is in charge to be implemented
PurchaseOrderRepositoryEntity — Interface that gonna receive the implementation of the database communication.
PurchaseOrderEntity — Represents the database model
PurchaseOrderRepositoryInMemory — implements the interface PurchaseOrderRepositoryEntity, this implementation gonna save the data in memory.
PurchaseOrderRepositorySpringData— implements the interface PurchaseOrderRepositoryEntity, this implementation gonna save the data in a physic database managed by SpringDataJPA
PurchaseOrderConverter — Convert the model object to the database model

Let’s create a new Integration Test to check the integration with our database, in this case, I’m using PostgreSQL.


Let’s check the implementation that has been improvement

PurchaseOrderRepositoryImpl receives the implementation purchaseOrderRepositoryInMemory but could be purchaseOrderRepositorySpringData


PurchaseOrderRepositoryEntitySpringData implements PurchaseOrderRepositoryEntity saving the purchase orders in memory




PurchaseOrderRepositoryEntitySpringData implements PurchaseOrderRepositoryEntity using the SpringData JPA saving the purchase orders in a Postgres database.




Application Layer
Is where startup the application there are all endpoints, entry points, and also integration tests.
This layer depends on the business and data-provider layer, and also contains the frameworks that will help us to implement the endpoints.





PurchaseOrderController — Receives the HTTP requests and invokes the use case CreatePurchaseOrder by DI.
PurchaseOrderRequest — This is a DTO — Data Transfer Object, that is in charge read a payload that sends in the body requests.
PurchaseOrderResponse— This is a DTO — Data Transfer Object, that is in charge to show the request results.
PurchaseOrderControllerConverter — Convert the model object to the DTO Request and Response.



Firstly let’s create a contract test, that gonna HTTP POST to save a new purchase order



Our test is done, now let’s check how are the implementation

PurchaseOrderController — Receive the POST HTTP

We can see that the CoinIntegration has not been implemented yet, só probably that this implementation is failed yet. So let´s implement the third-party-service layer.


Third-Party Layer
This layer is in charge of communicating with external services, in our case, we check the current price of the coin that we chose.
We can request the current price of the coin, in the CoinMarketCap API.


Third-Party Layer

This layer is in charge of communicating with external services, in our case, we check the current price of the coin that we chose.
We can request the current price of the coin, in the CoinMarketCap API.

Third-Party Module


Let´s see how are this implementation


CoinIntegrationImpl implements the CoinIntegration and receives an adapter that implements a client.




So, basically, to get the current price of any coin it needs to fetch by the symbol.



CoinMarketCapFeignClient — Spring Feign Client







View full communication between the layers







Check the full code in my GitHub repo


https://github.com/andrelucasti/start-project-onion-arch






Conclusion

The onion architecture is a form to build software with a focus on business rules, isn’t necessary to start with databases or APIs.
If you need to build an application with “longevity” that needs scalable, this design is a good way.
If you are developing an application that is a simple CRUD that doesn’t need scalable, I think this design is “too much”, my suggestion is that you use the TDD approach covering the use cases using integration tests.




References:

Jeffrey Palermo — https://jeffreypalermo.com/tag/onion-architecture/
Robert C. Martin — Clean Architecture
Herberto Graça — @hgraca


























Write & Read to Earn with BULB

Learn More

Enjoy this blog? Subscribe to andrelucas

12 Comments

B
No comments yet.
Most relevant comments are displayed, so some may have been filtered out.