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;
}
}
```