From the translator: this article was
written by Manuel Kiessling in September 2012, as the implementation
of Uncle Bob ’s
article on pure architecture with consideration for Go-specificity.

Before this article I translated its prototype -
look here . Since this article will actively use the description described in the article by Uncle Bob, it is better to start with it ... if you, of course, have not read it yet.
')
Unlike the first article, the name of the inner layer here includes the Domain instead of Entity (Entity) and in the translation I left this term to avoid confusion, since it also appears in the source code of the examples. I also translated Domain as a
Domain , because in my opinion this term here has a wider semantic meaning.
This part will describe the general concept and work with the inner layer.
In my opinion, the Dependency Rule is one of the most important rules to be applied when developing software, if you want to get a system that is easily tested, does not depend on the framework, UI or DB. Following this rule ultimately leads to a weak connectivity of the system components and a clear division of responsibility.
Loosely coupled systems
Systems consisting of parts of testable components and loosely coupled components can be expanded without any special problems and are transparent for understanding, modification, expansion and scaling. I will try to demonstrate the manifestation of these qualities when applying the Rules of Dependencies.
To do this, we implement a simple, but at the same time, a full-fledged Go-application simultaneously discussing when and how the concepts of "Pure Architecture" should be applied.
This application is the lowest possible version of the store, which allows you to receive a list of products related to the order via HTTP requests.
Source code, including some test coverage, can be
found on a githaba .
In order to keep the source code easy to understand, we will not implement other scenarios such as viewing products, placing an order or paying. In addition, I focused on the implementation of those parts of the code that help explain the essence of the architecture - thus, the code lacks many other things, such as full error handling. It also contains a lot of redundancy, but it allows you to read the code from top to bottom, without having to jump back and forth.
Application architecture
Let's start by analyzing the different areas of our application and their place within the architecture. The architecture of our application will be divided into 4 layers:
Domain ,
Scripts ,
Interfaces and
Infrastructure . We will discuss each layer from a high level position, starting from the innermost layer. Then we look at the low-level implementation of each layer, moving from the inner to the outer layers.
Domain or business of our application - people come to the store for shopping or in a more formal way: the customer adds goods to the order. We need to present these business objects and their rules as the code for the inner layer, the domain layer.
What to put and why
I decided to talk about customers, not users. Our application will of course have users, but they are of no interest when we talk about the Application Domain. I believe that if we want to seriously approach the division of responsibility, we must be very accurate in thinking about which layer to put the “user”, because the “user” deals with the Scenarios, but not with the business itself.
Not surprisingly, as software developers, we are used to looking at things from a software-formal point of view. Thus, arguing about architecture, I, in order to avoid mistakes, because of some unpleasant subtleties, I try to avoid metaphors concerning computers. What if we imagine that a business domain is not part of the program, but part of, say, a board game?
Imagine that we need to implement eBay or Amazon as a board game. The eBay site and eBay board game need buyers, sellers, products and bids — but only the eBay site needs users, sessions, cookies, authorization, etc.
This is quite a subtle difference, because when your program is small, the decision that users and clients are the same doesn't seem like a big deal. But this is one of those things that later will be difficult to fix. The reason is that for 99% of situations, the assumption that users and customers are the same means nothing, until the remaining 1% comes into play. Our application will illustrate this situation.
So, while orders and products belong to the Domain layer, users belong to the next layer - Scenarios.
What else belongs to the Script layer? Scenarios are a layer where use cases are realized, which arise from the fact that application users need to “do” something with the subjects of the base Domain. An example of a use case would be “the customer adds the product to the order”. To implement this and other use cases, you need to implement methods that “drive” the business entity.
Although this can be implemented in the domain layer, I recommend doing this in Scripts. The main reason for this is that the Scripts are application-specific, while the Domain entities are not. Imagine two applications: one of them allows customers to browse and buy things, and the second, which administrators use to manage and execute placed orders. While the entities of the Domain remain the same for both applications. The usage scenarios are very different: “add product to order” versus “mark order as delivered”. The domain and the Scripting layer together form the core of our application, representing the realities of the business with which we work. Everything else is implementation details that are not related to the specifics of our business. Our store can be implemented as a web site or a desktop application — as long as we don’t touch domain entities and usage scenarios, this is all the same business-oriented application.
We can switch the web service from HTTP to SPDY or DB from MySQL to Oracle - this does not change the fact that we have a store with customers who place orders consisting of products (Domain) and customers who can create orders, change the quantity goods and payment options (Scripts)
At the same time, this is a litmus test for our inner layers - should we change at least one line of code when switching from MySQL to Oracle?
If the answer is yes, then we have violated the Rule of Dependencies, making it so that one of our inner layers depends on the details in the outer layers.
There is also a place for code that works with the database or processing HTTP requests or external services. This place is a layer of interfaces.
For example, if our application becomes available as a website, the controllers that process the incoming HTTP request have their place in the interface layer, as they form the interface between the HTTP server and the application layer. These events from the outside world — caused by HTTP requests, mouse clicks in the UI, or remote procedure calls — create activity in our application. Without them, our Scenario methods and Domain entities would simply be a dead weight. But, since elements from the inner layers cannot interact (and generally know) as either with the outside world, interfaces are needed to convert events from the outside world into actions in the inner layers.
If we want to store store data (goods, orders and users in the database), then we also need an interface to the database. This is where the application of the Dependency Rule becomes especially interesting: if the code for working with SQL lives in the Interface layer and nothing from the inner layer can cause the outer layer, while saving is initialized at the Scenario level, how can we avoid violation Dependency Rules? We will analyze this in detail closer to the implementation of the code.
The last layer is called Infrastructure. Separating what belongs to Infrastructure and what Interfaces are not always easy. For example, both layers contain code that interacts with the outside world, such as code that communicates with a database. The determining factor here is that the Interface code is specific to your program, and the Infrastructure code is not and can be used in completely different applications. For example, the functions that process HTTP requests in our applications make sense only within the framework of this application, and the standard Go library for working with HTTP is a general purpose code and can be used in any other application. In this sense, most of the standard Go library will conceptually be in the Infrastructure layer.
Let's draw a line to create a list of all layers and parts of our application:
Domain:
- Entity Client
- Entity Item
- Entity Order
Scenarios:
- Entity User
- Scenario: Add product to order
- Scenario: Get a list of order items
- Scenario: The administrator adds the product to the order
Interfaces:
- Web service for processing goods / order
- Repository for Scripts and Domain Entities
Infrastructure:
- Database
- Code that handles database connections
- HTTP server
- Go standard libraries
As you can see, this list includes some elements that we haven’t talked about yet - admin scripts and repositories will be discussed in detail when we discuss implementation.
And the last thought before we dive into the code. If we look at how we divided our application, we will select a certain template. If we look at the layers and try to decompose them in two ways: business-dependent and application-specific code, we will see the following picture:
Infrastructure | Interfaces | Scenarios | Domain |
---|
Independent of application | Application specific | Application specific | Independent of application |
Independent of business | Independent of business | Business specific | Business specific |
For example, it is clear from this that code independent of the application and of business must be implemented in the Infrastructure layer.
The further we move to the left, the more low-level the code becomes (“send stream bytes to port 80”), the more we move to the right the more high-level the code becomes (“add product to order”).
Implementation
Domain
First we implement the Domain layer. As mentioned earlier, our application will be fully operational, but it will not be a full-fledged store. Thus, the code implementing the Domain will be short enough and we will implement it within the same file:
It is immediately obvious that this code has nothing significant in the dependencies - we only imported the “errors” package, since some methods return an error. Although the domain entities described here will eventually be strings in the database, there is no code associated with the database on this layer.
Instead, we define Go interfaces for three so-called repositories. The repository is a concept from DDD (Domain Driven Design): it is a way of abstraction from the fact that entities need to be saved or obtained through some kind of persistent storage mechanism. From the point of view of the Domain, the repository is simply a container through which domain entities are retrieved (FindById) or saved (Store).
CustomerRepository, ItemRepository and OrderRepository are just interfaces. They will be implemented within the Interfaces layer, since they implement the interfaces between the database and the application. This is how Dependency Rule can be implemented in Go applications — an abstract interface that does not apply to anything in the outer layers and is defined in the inner layers. Its implementation is determined in the outer layer. The implementation of the interface is done in the layer that should be used later. We will see this later when we get to the Scenario layer.
Thus, the Scenarios layer can refer to the concept from the domain layer through the repositories - while using only pure Go in the Domain layer. However, in fact, the code is executed on the Interfaces layer.
For each part of each layer, there are three questions of interest: where is it used, where is its interface, where is its implementation?
If we look at the OrderRepository, the answers are as follows: it is used in the Scripting layer, its interface belongs to the Domain layer, and its implementation belongs to the Interfaces layer.
On the other hand, the Add entity Order Order is also used on the Scenario layer and in essence it is an interface to the Domain layer. But its implementation is performed in the Domain layer, since the method doesn’t really need anything beyond its limits.
We also define the following 3 structures: Customer (customer), Order (order) and Item (product). This is a representation of our three domain entities. The essence of the Order (order) is implemented as two methods Add and value, and the value - an auxiliary function for internal use only. The Add method implements a business-specific function that is necessary for use in the Scripting layer.
Also in this code there are additional details that are significant when it comes to the architecture as a whole. As you can see, our application has several rules in different places and we should discuss which rules belong to which.
The first rule determines the addition to the order of goods that are not available - this is clearly a business rule. Forbidding a customer to add inaccessible goods to an order is a rule that applies to an order through an online store and, say, by telephone over the order via an operator. This rule is not application specific; it is a business requirement.
The same applies to the rule that orders cannot exceed the total cost of $ 250 - it does not matter whether our store is a website or a board game, this is a business rule that always applies.
Other rules are determined elsewhere. For example, the value of the goods must be stored in the database and the field type must be float, but this is a technical rule, not a business rule, and it does not belong to the Domain layer.
On the other hand, the interface code to the database when saving an order can perfectly save an order costing more than $ 250, since this is a restriction coming from business rules and the interface to the database should not care about such things. This example is an excellent illustration of why I love the principles of Uncle Bob so much - imagine that the limit on an order of $ 250 is implemented in a stored procedure in the database. Over time, these rules will spread to different places and become more difficult to maintain, the more your application. I prefer to always have all the business rules in one place.
Continued in the
next part