kdocs
GitHub
SC - Software Architecture
SC - Software Architecture
  • About
    • Architectural Requirements (RAs)
    • Architectural Perspectives
  • Software Design
    • Microservices
      • Patterns
    • Monolithic
    • C4 Model
  • Software Architectures
    • Clean Architecture
    • DDD (Domain Driven Design)
      • Strategic Modeling
      • Tactical Modeling
    • Event Driven Architecture
      • CAP Theorem
    • Hexagonal Architecture (Ports and Adapters)
  • Design Patterns
    • Behavioral
    • Creational
    • Data Access
    • Structural
  • Practices
    • Clean Code
    • SOLID
  • Others
    • CQRS
Powered by GitBook
On this page
  • About
  • Rules
  • 4 Layers
  • Entities
  • Use Cases
  • Interface Adapters
  • External Interfaces
  • Examples of Folder Divisions
  • Simpler
  • More complex
  1. Software Architectures

Clean Architecture

PreviousC4 ModelNextDDD (Domain Driven Design)

Last updated 4 months ago

About

It is a model with the objective of decoupling the application independent business rules (or domain), from external resources (like frameworks, DBs).

Independent Business Rules, are business rules that are independent of context (should be followed despite of where are executed).

Ex.: Business Rules that aren't valid only in specific Usecases.

Clean Architecture is Usecase driven.

In a way every Clean Architecture is also Hexagonal Architecture (Ports and Adapters), but the opposite is not.

In Hexagonal Architecture you don't have the distinction of usecases and entities, you only have the application (hexagon) which is the sum of usecases with entities.

The project folder names is completely irrelevant to the Layers.

You can have Layers without folders separating the files.

Rules

4 Layers

Entities

They are responsible for abstracting the Independent Business Rules, which can be from an object with methods to a group of functions.

What are Independent Business Rules?

Ex.:

  • Is CarPlate valid?

  • What is the distance between two Coords?

  • How much is the Ride Fare?

  • How the Ride Status change?

Domain Objects vs ORM Objects

Clean Architecture + DDD (Domain Driven Design)

Entities can be broken in Aggregate, Entity, Value Object and Domain Service by using DDD (Domain Driven Design).

Clean Architecture don't clearly specify what an Entity can be as DDD does.

DDD strongly complements Clean Architecture Entity Layer.

Anemic Domain

Anemic means that an Object only handles Properties (State) OR Behavior, but not both.

Use Cases

They orchestrate the entities and external resources, meaning they will be sort of a bridge between Entities and Interface Adapters.

Usecases are behaviors offered to the Clients. (You application exists because of your exposed Usecases)

Usecase names are related to [[Screaming Architecture]], which states to give names related to what it does.

Very rarely a Usecase A will call another Usecase B.

But this still increases coupling in the Usecase A, increases time to complete the Usecase A.

In very simple cases, a Usecase can be the same as CRUD operations.

But as complexity increases they will be less alike.

Interface Adapters

These are the bridges between Usecases (High level) and External Resources (Low level).

This is where you:

  • Treatment of requests and HTTP responses. (Dealing with parameters)

  • Access the Database. (All the SQL code belongs here)

  • Integrate with External APIs.

  • Read and Write to files. (Interact with Filesystem)

  • Convert data. (Like convert to CSV, PDF)

External Interfaces

Here lies the lowest level of abstraction.

Where you actually interact with technology, with the components that connect to the Database, that deal with the HTTP requests, that interact with the filesystem or access S.O resources.

The ideia in here is to abstract only some level of the technology used, so that the Interface Adaptors won't know for instance if they will connect to a Postgres or MySQL database.

DatabaseConnection.ts
export default interface DatabaseConnection {
    query(statement: string, params: any): Promise<any>;
    close(): Promise<void>;
}
PgPromiseAdapter.ts
export default class PgPromiseAdapter implements DatabaseConnection {
    connection: any;
    
    constructor() {
        this.connection = pgp()('postgres://...');
    }
    
    query(statement: string, params: any): Promise<any> {
        return this.connection?.query(statement, params);
    }
    
    async close(): Promise<void> {
        await this.connection?.$pool.end();
    }
}

But there is a certain level of restriction for decoupling External Interfaces.

Ex.: You may not be able to write a contract for the Database connection that works on unsimilar technologies. (Like Postgres and Mongo)

Interface Adapter vs External Interfaces

In Hexagonal Architecture (Ports and Adapters), there is no difference between Interface Adapters and External Interfaces, they are the same.

An example of mixing Interface Adapters with External Interfaces would be:

Having the code that connects to the Database inside a Repository like this.

UserRepositoryDatabase.ts
class UserRepository {
    async getUserById(userId: string): Promise<User> {
        const connection = pgp()('postgres://...');
        const user = connection.query('SELECT * FROM user WHERE id = $1', [userId]);
        await connection.$pool.end();
    }
}

So, there is a certain level of coupling of Postgres with our Interface Adapter that is called from the Usecase.

Examples of Folder Divisions

Simpler

/src
  ├── /core
    ├── /exceptions
    └── /entities
      └── <entity-name>.ts
  ├── /usecases
    └── <usecase-name>.ts
  ├── /infra
    ├── /repositories
    ├── /gateways
    ├── /http
    └── ...
  └── main.ts
/tests
  ├── /unit
  ├── /integration
  └── /e2e

More complex

The inner layers (high level) should not know implementations of external layers (low level), which is .

Entity ≠\neq= ORM object entity. (Which is a mere database table representation)

So, instead of calling Usecases inside another Usecase, use Design Pattern to decouple these Usecases by using a bus/channel like a queue, for communication between them. (Not necessarilly an external tool like RabbitMQ)

Clean Architecture on Frontendbespoyasov_
Clean Coder Blog
Logo
D (Dependency Inversion Principle - DIP)
Mediator
Logo