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