Estamos utilizando o modelo baseado em Domain Driven Design, com ou sem Event Sourcing.
Este modelo se baseia em 3 camadas base que se comunicam de forma top-down, ou seja, a camada mais superior irá interagir com a mais inferior, mas nunca o contrário.
Todo o cenário é baseado na imagem a seguir:
A estrutura de pastas seguirá o modelo abaixo, mas você pode checar em tempo real acessando o diretório de exemplo:
project-x
├── Dockerfile
├── app-config.ts
├── ci-config.yaml
├── desktop.ini
├── package.json
└── src
├── data
│ ├── connections
│ │ ├── mongo.ts
│ │ └── sql-server.ts
│ └── repositories
│ ├── entity-x.ts
│ └── entity-y.ts
├── domain
│ ├── domain-base.error.ts
│ ├── entity-x
│ │ ├── entity.ts
│ │ ├── errors
│ │ │ └── EntityXError.ts
│ │ ├── events
│ │ │ ├── entity-x-was-created.ts
│ │ │ ├── entity-x-was-deleted.ts
│ │ │ ├── entity-x-was-finished.ts
│ │ │ └── entity-x-was-updated.ts
│ │ └── structures
│ │ ├── enums
│ │ │ └── Data.ts
│ │ └── interfaces
│ │ ├── IData.ts
│ │ └── IFilter.ts
│ ├── entity-y
│ │ ├── entity.ts
│ │ ├── errors
│ │ │ └── EntityYError.ts
│ │ ├── events
│ │ │ ├── entity-y-was-created.ts
│ │ │ └── entity-y-was-updated.ts
│ │ └── structures
│ │ ├── enums
│ │ │ └── Data.ts
│ │ └── interfaces
│ │ ├── IData.ts
│ │ └── IFilter.ts
│ └── index.ts
├── index.ts
├── libs
│ ├── cpf-validator.ts
│ └── helper-abc.ts
├── presentation
│ ├── app.ts
│ ├── routes
│ │ ├── route-a.ts
│ │ └── route-b.ts
│ └── server.ts
└── services
├── entity-x.ts
└── entity-y.ts
project-x
: É a pasta inicial que vai conter o nome do projeto, tudo estará dentro desta pasta- Arquivos de estrutura: Abaixo da raiz, no primeiro nível, vamos ter os dotfiles necessários para configuração de CI/CD, Docker e outras informações; aqui será aonde vamos ter o
package.json
e otsconfig.json
.env
ou.envrc
: Variáveis de ambiente devem ficar em um arquivo.envrc
para execução local, toda a execução em imagem teremos um CI que vai passar as variáveis de ambiente para o container. Todo.envrc
deve ter um.envrc-sample
com a lista das variáveis de ambiente daquela aplicaçãosrc
: Pasta que conterá todo o código fonte da aplicaçãoindex.ts
: É o entrypoint da aplicação, será nele que a aplicação vai começardata
: É a camada de dados da aplicação, conforme especificada em sua seçãoconnections
: Aqui ficarão todos os arquivos referentes a conexões com bancos de dados e outras fontesrepositories
: Aqui ficarão os manipuladores dos clientes de fontes de dados existentes emconnections
, esta será a pasta que conterá os arquivos que serão consumidos pela camada de serviços
domains
: Será a coluna de domínio, conforme especificada em sua seçãoindex.ts
: Retornará um objeto com todas as entidadesdomain-base.error.ts
: O erro base do domínio que todas as entidades devem estender<entity-x>
: Cada pasta dentro da camada de domínio terá o nome de uma entidade deste domínioentity.ts
: Toda entidade terá um arquivoentity.ts
que será o arquivo que juntará todos os comandos, eventos e erros. Em teoria, será o local aonde as demais camadas irão buscar a instância do domínio em sierrors
: Aonde ficarão as classes de erro da entidade, todas devem estender o arquivo base de erroevents
: Somente utilizado no caso de event-sourcing, será aonde estarão armazenados as ações que podem ser executadas sobre esta entidade em forma de eventos, todos os arquivos devem ter o nome<nome-da-entidade>-descrição-do-evento.ts
structures
: Dentro desta pasta ficarão todas as estruturas de controle como: interfaces, enums e etc. Cada uma delas em uma pastainterfaces
ouenums
. A pastastructures
pode ser criada dentro de qualquer pasta da árvore que necessite de uma estrutura de controle.
libs
: Aqui ficarão os arquivos de bibliotecas, helpers e qualquer outro arquivo que sirva de repositório de utilidadespresentation
: A camada de apresentação ficará descrita aqui, conforme descrição de sua seçãoapp.ts
: Neste arquivo serão carregadas todas as rotas da aplicação, exportará o modelo base de um webserverserver.ts
: Irá receberapp.ts
e irá iniciar um servidor webroutes
: Aqui ficarão os handlers das rotas, toda a rota deverá ter o nome<verbo>-descrição.ts
services
: Camada de serviços conforme descrição<entity-x>
: Para cada entidade emdomains
temos que ter um arquivo aqui, que será o responsável por se comunicar com a camada de dados e manipular os dados no banco seguindo o comando que for executado pelo domínio
- Arquivos de estrutura: Abaixo da raiz, no primeiro nível, vamos ter os dotfiles necessários para configuração de CI/CD, Docker e outras informações; aqui será aonde vamos ter o
A camada de apresentação será responsável por receber as chamadas do serviço de gateway e encaminhar para a camada de serviços.
Esta camada também vai ser responsável por receber as respostas da camada de serviço e apresentar para o usuário em forma de JSON ou qualquer outro content type.
Além disso, todos os erros que vierem das camadas inferiores, sejam eles relativos ou não à camada de apresentação, serão tratados e exibidos pelo usuário nesta mesma camada.
A camada de serviço será responsável por fazer a comunicação com todo o resto da arquitetura. Cada serviço receberá pelo menos uma instancia de um repositório da camada de dados, podendo receber também outros serviços.
Os serviços serão chamados a partir da API que está na camada de apresentação e terão acesso ao domain bus para instanciar novas entidades.
A camada de dados é a responsável pela comunicação com fontes externas de dados (APIs, DBs).
Nesta camada estará a pasta connections
que será um agregado de conexões de bancos de dados que podem ser utilizadas.
Aqui estarão os arquivos que gerarão e conectarão com os bancos de dados, mas não vão realizar nenhum tipo de operação.
Os repositórios serão os manipuladores dos clientes de bancos de dados localizados na pasta de conexões descrita acima. Serão eles que vão realizar as operações no banco de dados e retornar os dados propriamente ditos.
A coluna de domínio será aonde estará descrito, de fato, o domínio da aplicação com suas entidades, eventos, comandos, regras, erros e afins. Ela é responsável por representar os objetos de negócio dentro do código.
Conterá bibliotecas e helpers que podem ser utilizados em toda a aplicação independente da camada.