https://dev.to/barrymcauley/onion-architecture-3fgl

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6914272e-d85c-4849-be27-ad5af8327f4a/b4fi64hld9euzmglcibq.jpg

Onions are a delicious vegetable and are a core ingredient in cuisines around the world. Perhaps then you are wondering, why are we discussing them in the context of software engineering? First introduced by Jeffrey Palermo in a series of blog posts, Onion Architecture guides software engineers to model their business logic in a core collection with no coupling to the outer concerns, such as database choice or how the user interface operates.

What does Onion Architecture look like? It may come as a surprise.

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/bf2547aa-5963-479e-916f-4fe2e9e371e8/onion_architecture.svg

It looks very similar to an onion with layers wrapping around a central core. Each of these layers represent a specific duty within the overall function of a service. Similar to an onion, you can only access the core by going through the outer most layers and it is that narrative which informs us of the architectures purpose - to direct the flow of coupling towards the centre from the outside in.

So, like a typical onion, let's work our way into the core and hopefully avoid any tears along the way. The three outer layers are those which are not directly related to our business logic but depend upon on it fulfil their own purpose. They can change often and thus are separate from our core application logic.

These layers are: Infrastructure, where our database, file system, or any external web service we depend on live. Tests: unit, integration, end-to-end. How we validate our business cases. Finally, User Interface, how our users interact with the code we have built. These layers are the ones which interact with the first layer of our "application core" and that is the Application Services layer (sometimes known as the Transport Layer). Within this layer, we define what our service can do through a series of contracts.

Inward moving, we encounter the Domain Services layer. In this layer is where the majority of our business logic lives, it carries out the operations to turn A into B, input into output, egg into chicken. It achieves this through interacting with the final layer, the Domain Model layer which is the representation of the high level data objects we use.

Let's walk-through an example on how we can solve a real-world task such as processing a financial transaction to see how we apply the Onion Architecture. from the outside, in.

An Example - Buying A Coffee

One outer layer which may surprise many is Infrastructure. Is the database we use or an external dependency not part of our domain model layer? A very valid question so let's explore it further.

Take for instance we have the following definition of a user account in some NoSQL database:

{
    "id": "some_user_id",
    "balance": 500.00,
    "currency": "GBP",
}

So when we do a query and wish to interact with this, it would be common sense that we would create a model object to marshal this json into such as:

type Account struct {
    ID       string    `json:"id"`
    Currency string    `json:"currency"`
    Balance  float64   `json:"balance"`
}

Makes sense, right? Lets say however that the team decides that NoSQL isn't up to scratch and some relational goodness is the trend of the month. Well, thanks to the power of the onion architecture, as long as the new relational structure provides the fields required as defined in your domain model contract, the business logic of your application does not need to change and you can continue to provide the functionality your users need. This applies to any external dependency or data-storage the application interacts with the key takeaway being:

Externalise your dependencies and decouple them through contracts.

Now we have a domain model representation of an account, let's need to define a use-case for a financial transaction and the steps involved when our user, Andre, decides to buy a coffee: