---
# System prepended metadata

title: Hexagonal Architecture + DDD Tips

---

estrutura de pastas:

```
/src
    /commons
    /config
        /database
    /mechanisms
    /modules
        /user
            /application
                /dto
                    register_user.dto.ts
                    query_list_user.dto.ts
                /use-cases
                    register_user.use-case.ts
                    inactive_user.use-case.ts
                    list_users.use-case.ts
                    delete_user.use-case.ts
            /domain
                /entities
                    user.entity.ts
                /repositories
                    user.repository.ts
            /infrastructure
                /mappers
                    user.mapper.ts
                /models
                    user.model.ts
                /repositories
                    user.repository.impl.ts
            /presentation
                /controllers
                    user.controller.ts
            user.module.ts
```



---


Vamos Começar falando sobre o DDD em si, Domain Driven Design (Desigin Orientado ao Domínio), e o que isso quer dizer?.

Isso significa que tudo que for construído será baseado na regra de negócio de cada módulo. o que implica dizer que vamos abandonar o modelo de API RestFull, então jamais teremos uma rota ```PUT /users/:id```  que dará o poder do cliente alterar qualquer coisa do usuário.

### 🧐Pense Comigo

Imagine que o usuário tenha vários campos como email, senha e telefone, e para cada campo desse o função de editálos terá várias regras de negócio diferentes. Se utilizássemos o padrão restFull nesse caso poderíamos ter um ```user.service.ts``` e dentro dele ter um método ```update``` que tem todas as regras de negócio de campos diferentes em um só lugar, o que facilmente tornaria nosso arquivo de ```service``` com 2000+ linhas e por sua vez tornando o código ~~ilegível e imanutenível~~ 😢.



---


Para resolver esse problema e de várias outras arquiteturas que chegamos com o DDD e consigo os famosos **use-cases** (casos de uso), onde cada rota da API é orientada a uma inteção do usuário, entao ao contrário do texto anterior cada regra de negócio e funcionalidade ficam em lugares separados tornal fácil a manutenção e leitura.

Já que não vamos mais ter uma rota ```PUT /users/:id``` que possibilita a edição de vários campos com várias regras de negocio diferentes, teremos agora uma rota para cada intençãoa. Então se o cliente quiser alterar o email do usuário ele terá uma rota como ```PATCH /user/:id/change-email``` e com o body:
```
{
    email: "new@email.com"
}
```

e o controller vai chamar o método "execute" do use case,

#### Exemplo: 

```
import {
  Controller,
  Post,
  Body,
  Request,
} from '@nestjs/common';
import { ChangeUserEmailUseCase } from '../../application/use-case/change_user_email.use-case';
import { ChangeUserEmailDTO } from '@user/application/dtos/change_user_email.dto';
import { DomainException } from '@commons/exceptions/domain-exception';

@Controller('user')
export class UserController {
  constructor(private changeUserEmailUseCase: ChangeUserEmailUseCase) { }

  @Post(':id/change-email')
  async create(@Body() dto: ChangeUserEmailDTO, @Request() req) {
    try {
      return await this.changeUserEmailUseCase.execute(
        dto.email,
        req.userRequester
      );
    } catch (error) {
      if (error instanceof DomainException) {
        error.toHttpException();
      }
      throw error;
    }
  }
}

```

então as regras de negócio para alterar o email do usuário estarão dentro do use-case ```ChangeUserEmailUseCase``` como:

- O usuário só pode alterar o email de outros usuários sem ser ele mesmo caso ele seja administrador.
- Não é possivel alterar o email para um email ja utilizado por outro usuário.


Por sua vez o use-case utiliza os métodos do `user.repostiroy.ts` para manipular dados de uma fonte de dados que pode ser um banco de dados, banco em cache, arquivo csv ou memária da própria aplicação. O mais natural que vemos mercado a fora é o banco de dados.

##### Então `user.repository` deve ter obrigatoriamente esses 5 métodos

- `createUser(name: string, email: string, phone: string, active: boolean): UserEntity{}`
- `findUserByID(id: string): UserEntity{}`
- `findAllUsers(query: { [key in keyof UserEntity]?: UserEntity[key] }): UserEntity[]{}`
- `updateUser(user: UserEntity, values: { [key in keyof UserEntity]?: UserEntity[key] }): UserEntity{}`
- `deleteUser(user: UserEntity): UserEntity{}`

Nesses métodos do repository não deve conter nenhum tipo de regra de negócio, devem ter apenas uma função que é manipular dados de uma fonte de dados. Então **não é permitido** criar um método no repository como `changeEmail(user: UserEntity, new_email: string)` pois isso é trabalho do use-case.

Então nesse nosso o use-case `ChangeUserEmailUseCase` irá utilizar os métodos `findAllUsers` para verificar se já existe algum usuário com o email a ser alterado, e o método `updateUser` do repository para alterar finalmente o email do usuário.



## Entity x Models

Na arquitetura hexagonal, a separação entre Entity e Model é proposital e reflete a distinção entre domínio puro e infraestrutura/persistência. Vamos entender o papel de cada um com base no seu projeto:

### Entity

#### Responsabilidade:

- Representa a lógica e as regras do domínio da aplicação, sem depender de frameworks, banco de dados ou tecnologia externa.

- Contém invariantes do negócio (por exemplo: "um usuário não pode ser criado sem e-mail").

#### Exemplo:

```
class UserEntity {
  constructor(private name: string, private email: string) {
    if (!email.includes('@')) throw new Error('Invalid email');
  }

  // regras de negócio, métodos do domínio, etc.
}
```

#### Características:

- Simples classes ou objetos de domínio (POJOs).

- Não tem nenhuma dependência com ORM (como Sequelize, TypeORM) ou banco de dados.

- Portável entre diferentes tipos de infraestrutura.

### Model

#### Responsabilidade:

- Representa a estrutura de dados usada para persistência, geralmente acoplada a um ORM ou banco de dados.

#### Exemplo

```
@Entity()
export class UserModel {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  email: string;
}
```

#### Características:

- Utiliza decorators e configurações específicas de bibliotecas como TypeORM, Prisma, Sequelize.

- Depende da infraestrutura para funcionar.

- Foca no armazenamento e recuperação de dados, não na lógica de negócio.

#### 🔄 Por que separar Entity e Model?



| Motivo | Explicação |
| -------- | -------- |
| Isolamento da lógica de negócio     | A Entity pode ser testada, reutilizada e evoluída sem depender de banco de dados ou framework.     |
| Independência de tecnologia | Você pode trocar o ORM ou banco sem precisar mudar sua regra de negócio. |
| Facilidade de manutenção e testes | O domínio é testável de forma isolada, com menos dependências. |
| Responsabilidade única (SRP) | Model lida com persistência; Entity com lógica de negócio. |


🧩 Ligação entre Model e Entity: o Mapper

O arquivo /infrastructure/mappers/user.mapper.ts provavelmente serve para converter entre UserModel e UserEntity.

```
class UserMapper {
  static toEntity(model: UserModel): UserEntity {
    return new UserEntity(model.name, model.email);
  }

  static toModel(entity: UserEntity): UserModel {
    const model = new UserModel();
    model.name = entity.name;
    model.email = entity.email;
    return model;
  }
}
```

