The importance of Architecture
Like a building, software needs a foundation in order to be built. Before starting with the foundation, as with any engineering work, you need an architect that will create the architecture. This is the normal way to go when building something. Going back to software tho, you don’t need to be a certified architect in order to build an application. But, if you want it to scale, to easily get help from other programmers or to update it to keep up with the competition, you may find out that bad architecture makes a hard life. And you should pay attention to it.
The reason it is so important, among all the above, is that it is the first thing you begin with. Even if you don’t pay attention to it and start coding, your code will still be architectured in a way (though it may not be specifically named).
In this blogpost, I’ll show you three general architecture types when it comes to organizing your code. We will begin with a naive approach where you organize your files in a technical layer approach. We’ll continue with a feature layer approach, where each folder represents a big feature of the project, and then finish with a screaming architecture approach, my personal preference.
The examples follow the NestJS framework, hence the file naming.
Architectures breakdown
Package by layer
As the name says, this architecture style focuses on structuring your code base around technical layers. If we go for a controller-service-repository approach, then we will have three big folders like controllers, services, repositories, with other folders for models and infrastructure code.
While this approach is easy to implement and to grow initially, it will soon be overwhelmed as the application grows and it will make hard for developers to figure out dependencies and what files link to eachother.
How it looks from the outside: Looking at it, we may understand that the project has something to do with files and users, while it uses the controller-service-repository approach. This is pretty much it, we don’t know what business logic it employs or what else it does, you need to check the files for it.
src/
├── controllers/
│ └── file.controller.ts
│ └── user.controller.ts
├── services/
│ └── file.service.ts
│ └── user.service.ts
├── repositories/
│ └── file.repository.ts
│ └── user.repository.ts
├── models/
│ └── file.model.ts
│ └── user.model.ts
└── main.ts
Package by feature
With this approach, we focus on showing the features of the system. If you have a Bank application, you may have some big modules like Cards or Subscriptions, where you put all the files related to that feature there. It doesn’t offer much explanation though and the files may or may not contain more logic than needed. The breakdown isn’t so pronounced, though it is more clean than the previous layer one.
How it looks from the outside: The application clearly has two important logic elements, the users and the files. This makes easy to develop individual parts of the system, because all the code related to that feature is in the same place. While you have all the files related to a feature in the same place, it still doesn’t explain the bussines domains as clearly.
Screaming Architecture
Screaming architecture implies screaming at the reader on what the code does. It focuses on domain and bussines logic above everything else. It decouples the technical side from the domain logic and allows you to clearly express your intent on what the application does.
How it looks from the outside: The reader can clearly see that the application is about managing users and files. The folder names are descriptive, same with each components that are container. It also takes from the feature based approach, but on a more granular level, where folders like upload-file and download-file express clearly what features they are, and also should follow the single responsibility principle. One downside for screaming architecture is that it can get fairly complicated if not properly managed.
src/
├── file-management/
│ ├── upload-file/
│ │ ├── upload-file.controller.ts
│ │ ├── upload-file.service.ts
│ │ └── upload-file.use-case.ts
│ ├── download-file/
│ │ ├── download-file.controller.ts
│ │ ├── download-file.service.ts
│ │ └── download-file.use-case.ts
│ └── file.module.ts
├── user-management/
│ ├── create-user/
│ │ ├── create-user.controller.ts
│ │ └── create-user.use-case.ts
│ ├── get-user/
│ │ └── get-user.service.ts
│ └── user.module.ts
└── main.ts