Simple Clean Architecture

Genki Sano
8 min readJun 3, 2021

--

This article introduces the concept of Simple Clean Architecture, software architecture for real-development use in Unity / C# business applications.

Why do we need software architecture?

Software can be implemented in any way you want. As long as you are doing personal development, and as long as you know everything about the code, you are free to do any implementation you want. However, you may get a problem when multiple people are working together on a large codebase in a real business situation. The genius code you wrote might be difficult to understand for others, and it might slow down your team’s development agility. The mission of software architecture is to set frames/rules on the structure of software to make the code consistent, thereby increasing development speed and quality.

The Simple Clean Architecture (SCA) is designed with the following goals for startup development. Time is a key factor for startups. Make your development speed as fast as possible for your business success. To achieve this…

  • Code should be organized/layered by certain rules so that multiple members will write the code in the same style.
  • Even if multiple members participate, the implementation should be loosely coupled and less interdependent so that changes do not interfere with each other.

What is Simple Clean Architecture?

Simple Clean Architecture (SCA) is based on the idea of Clean Architecture. SCA gives you a guideline for actual implementation while normal Clean Architecture just explains a concept. We simplified redundant parts in Clean Architecture and added the concepts of “Dependency Injection” and “Reactive Programming” to make architecture ready to start (actual) implementation.

The original idea of Clean Architecture is a fundamental concept to keep components loosely coupled. We recommend that you check the original concept of Clean Architecture as well.

https://www.oncehub.com/blog/explaining-clean-architecture
https://www.freecodecamp.org/news/990c014448d2/

Also, as mentioned earlier, SCA has the concepts of Dependency Injection and Reactive Programming. These are important concepts to make the architecture ready for actual implementation. These techniques are common approaches in software development, so we also recommend that you look into the basics.

To summarize, what SCA is…

  • A simplified and ready-to-implement guideline of the Clean Architecture
  • Dependency Injection and Reactive Programming are introduced for actual implementation

Structure of Simple Clean Architecture

The following diagram is introduced in Uncle Bob’s original blog about Clean Architecture (link).

Clean Architecture is just a conceptual guideline to keep your project clean by reducing the dependencies between components. Therefore, there are many styles of “Real implementation in Clean Architecture”. Based on the SOLID principles of Clean Architecture, SCA especially emphasizes the following points.

  • Your component can only depend on one inner layer from the outer layer (layer design pattern)
  • Each component should be coupled through the interface if the coupling is across the layers. And dependency must be solved by Dependency Injection
  • Eliminate dependencies/deep-coupling by using Reactive Programing

In addition to the above points, we simplify components in Clean Architecture. We just use these 5types of component in SCA; Entity / Usecase / Presenter / Gateway / View.
Let’s take a look at each component.

Entity

Entity is a basic and elemental component at the center layer in the Clean Architecture, so it can’t have dependencies on any of the higher layers. Entity is responsible for encapsulating the most basic business rules, including primitive objects and methods. If you are familiar with DDD (Domain Driven Design), Entity is the set of Entity, ValueObject, and DomainService in DDD.

Important points are 1. Entity is a set of primitive objects and methods for your business application, and 2. Entity must not have any dependency on other components in the upper layers.

Usecase

In Clean Architecture, usecase is in the 2nd layer from the center. The 2nd layer is in charge of the Application Business Rules. Usecase manipulates Entities. Furthermore, Usecase can also achieve more complicated manipulation using Gateway, such as “acquiring the data defined as Entity in the database through the Gateway and processing it”. Therefore, Usecase can depend on the Interface of Gateway. Also, Usecase will be called by Presenter through Interface.

Gateway

In the Clean Architecture, the 3rd layer from the center is the Interface Adapters layer, where the components responsible for “connecting business rules with the world outside the application, such as the UI, DB. Gateway in the 3rd layer is responsible for interaction with DB and external libraries. For example, suppose you define an interface called IDBGatewaythat retrieves the data defined in Entity from the database.

public interface IDBGateway{
int GetValue(string key);
void SetValue(string key, int value);
}

In this case, you can decide whatever database, such as MySQL, MongoDB, you will use for your actual implementation. The difference between these databases itself can be implemented as MySQLGateway : IDBGatewayor MongoDBGateway : IDBGateway,which are inheriting IDBGateway. The important thing is that the modification of this logic inside does not affect the other layers; you can make this change by simply replacing MySQLGateway to MongoDBGatewayby Dependency Injection. This dependency injection concept is the same for the other components (Usecase and Presenter).

Presenter

Presenter is also in the 3rd layer in Clean Architecture (as same as Gateway). And since the 3rd layer is in charge of “connecting the business rules to the outside world”, Presenter is responsible for interaction with the UI (View). Presenter can refer to Usecase through the interface, conveys inputs from View to Usecase to drive business logic, and conveys update of Entity from Usecase to View. And we use Reactive Programming as a way for this communication path. Presenter has a Reactive Property that publishes Entity updates so that the UI View can be updated without any dependency by subscribing to the property.

View

View belongs to the outermost layer, “Frameworks & Drivers” in Clean Architecture. View is responsible for manipulating the UI, i.e. inputting actions through the UI and outputting the update of Entity.

View can depend on Presenter through the interface. View will call methods in Presenter to achieve action to the internal logic. On the other hand, View subscribes to the Entity’s update through Presenter, and View will update the UI on display when the subscribing info is updated. This can make View less coupled from Presenter. Since the View is in the most outside of the layers, View can’t have any dependency on the components in the other layers.

To summarize the above in a diagram, the relationship between the components is as follows

Implementing SCA in Unity

SCA is not limited to a specific programming language or platform. If you want to keep components loosely coupled in a large-scale development project, you can apply the design concept of SCA.

On the other hand, one of the most practical usages of SCA is for projects on Unity. Usually, development on Unity can easily go into a nightmare without software architecture such as…

  • A Monobehaviour class depends on other Monobehaviour classes, (and those classes depend on others…) and no one knows how all of the Monobehaviour objects are linked together…
  • Dependencies to Monobehaviour are solved by public and/or [serializefield]. Variables are assigned from Inspector, and all the references got lost by someone’s change in the scene/script...
  • Multiple people edited one scene file (.unity) to add new objects and get conflict on git (and to resolve the conflict in the scene file is a nightmare, as you may know)

These happen as a result of over-reliance on Monobehaviour and manipulation from Unity’s editor. By keeping your code as organized as possible in the pure-C# world (not too much sticking into Unity’s platform), and by adding fewer components to your scene and fewer references to Monobehaviour, you will get a clean project in large-scale development.

Specifically, the implementation of SCA in Unity looks like the following

  • Separate components into layers defined on SCA
  • Limit the inheritance of Monobehaviour to “View and Presenter only”.
  • Using UniRX for Reactive Programming
  • Introduce classes that perform DI and little editing of the Unity scene as much as possible.

The following shows the flow of data between components.

Gateway does not appear here just because Gateway is called from Usecase and that is managed in a pure C# world without any intervention of Monobehaviour or Unity. The important thing is that the View only refers to the Presenter, and the Presenter only refers to the Usecase. To achieve this, we use Reactive Programming to propagate information in the reverse direction of dependency, such as from Presenter to View.

The rules for each component in SCA on Unity are summarized as follows;

View

  • View can depend on the Presenter through its interface
  • View can’t depend on another View.
  • View can’t depend on Use Case, Gateway
  • View can inherit Monobehaviour

Presenter

  • Presenter can depend on Usecase through its interface
  • Presenter can’t dependent on View, Gateway
  • Presenter can inherit Monobehaviour
  • Presenter may rely on other presenters through its interface (but try to avoid as much as possible)

Usecase

  • Usecase can depend on Gateway through its interface.
  • Usecase can’t be dependent on View, Presenter
  • Usecase can’t inherit Monobehaviour
  • Usecase may depend on other Usecase through its interface (but try to avoid as much as possible)

Gateway

  • Gateway can’t depend on View, Presenter, Usecase
  • Gateway can’t inherit Monobehaviour
  • Gateway may rely on other Gateways through interfaces (but try to avoid as much as possible)

A sample implementation of SCA for Unity is available on GitHub for your reference.

Comparison with other architectures

Why do I recommend SCA and not MVP or MVVM? The answer is “Because MVP and MVVM tend to make bigger/more complex models. This is not saying that MVP and MVVM are bad. As long as MVP/MVVM or other software architectures give you advantages that outweigh their disadvantages, that’s totally fine. But as mentioned earlier, the code size of the business logic part (model) will be getting huge if your project manages complicated logic. In SCA, the part corresponding to the model in MVP/MVVM is divided into Gateway, Usage, and Entity, so this “huge/complex model problem” is less likely to occur. In other words, SCA is “a more detailed definition of the model in MVP and MVVM”.

If you’re facing a problem, like “I can’t keep my code organized with MVP! but if I go with Clean Architecture seriously, I’ll end up with too many components such as Repository, Use input port, Data Access, etc..”. In such case, Simple Clean Architecture would be one of the options; “Simplified & ready-to-implement Clean Architecture design pattern = Simple Clean Architecture”.

Let’s make clean & less coupled code to maximize your business efficiency!

--

--

Responses (1)