Open omarkdev opened 7 years ago
Eu consideraria "Api" e "Dashboard" como aplicações. Então eu costumo criar uma pasta dentro de App chamada "Applications" e dentro dela eu tenho cada aplicação que o Framework fornecerá.
No caso, seria algo como:
Applications/Api Applications/Backend Applications/Dashboard Applications/Register
E dentro de cada uma delas teria cada Controller, Requests, Middleware, Rotas, etc.
Espero ter ajudado.
[]'s
Bem, primeiramente creio que seja válido dizer que a arquitetura do codecasts/laravel não é exatamente DDD, é uma adaptação inspirada no DDD. Isso é importante ser dito porque o DDD em si é orientado por 3 camadas: Application, Domain e Infrastructure. Não que o skeleton citado seja "errado", ele simplesmente usa uma arquitetura própria e isso é tão normal quanto os frameworks terem um skeleton inicial próprio.
A arquitetura DDD implica neste tipo de organização:
- src/
- Acme/ (seu top-level namespace conforme as orientações do PSR-4; opcional uma vez que pode ser suprimido no composer.json)
- Context/ (um bounded context ou módulo, faz uso da Ubiquitous Language)
- Application/ (camada da aplicação)
- Domain/ (camada do domínio)
- Infrastructure/ (camada de infraestrutura)
- tests/
Idealmente cada módulo é arquitetado separadamente de modo que sejam reaproveitáveis. 😃
O DDD é arquitetado assim justamente para separar as camadas, pois à medida que você foge disso não fica muito claro o que é aplicação, o que é domínio e o que é infraestrutura - as chances de você ter quebrado o desacoplamento são grandes. O Hexagonal Architecture dá um passo além nesta arquitetura e coloca cada port em seu próprio namespace também, de modo que você pode ter um port Http
, um port Api
, um port Cli
, etc.
Note que uma API no Hexagonal é um port. No DDD ela é aquilo que chamam de delivery mechanism e está dentro da camada de infraestrutura.
Um outro ponto importante a ser considerado aqui é que API não é nem aplicação nem conceito de domínio, é pura e simplesmente um dos contextos pelo qual você pode consumir uma determinada aplicação ou módulo. 😃
Tanto o DDD como o Hexagonal são orientados aos conceitos de domínio (domain concerns).
Num e-commerce por exemplo, você poderia ter um bounded context para encomendas (orders), outro para pagamentos (payments), outro para inventário (inventory), e por aí vai. Seguindo o exemplo acima, cada bounded context seria um módulo próprio. Cada um deles vão ter a própria definição do contexto de API. Note que a infraestrutura disso não é necessariamente separada, você pode ter isso tanto numa infraestrutura monolítica como numa infraestrutura de micro-serviços (microservices).
Vou tomar aqui um exemplo citado no livro Domain-Driven Design in PHP referente aos delivery mechanisms de uma aplicação arquitetada em DDD:
- src/
- Billing/
- Infrastructure/
- Delivery/
- API/
- Laravel/
- Console/
- Symfony/
- Web/
- Silex/
- Slim/
Essa é só a parte de infraestrutura do bounded context de pagamentos (billing) de forma bem simplificada. Note que esse módulo pode ser usado via web, via API e via CLI. Observe ainda que ele usa 4 frameworks ao mesmo tempo, e isso só é possível através do desacoplamento dos conceitos de infraestrutura - e é justamente por isso que o DDD e o Hexagonal separam a infraestrutura.
Isso reforça o ponto anterior de que API não é uma aplicação nem um conceito de domínio.
Em relação ao Dashboard, seguindo os conceitos de domínio este certamente seria um bounded context de relatórios (reporting). Seguindo o namespace da organização Acme
estaria no namespace Acme\Reporting
. Note que o termo dashboard está mais relacionado com a apresentação dos dados, em conceitos de domínio isso está dentro do módulo de relatórios. 😃
Observe que tudo é orientado à linguagem do domínio a qual Eric Evans denomina Ubiquitous Language no Big Blue Book. É interessante citar que inclusive faz parte das design guidelines do DDD e do Hexagonal estruturar os namespaces seguindo esta linguagem de domínio e evitando usar nomenclaturas de patterns ou building blocks como value objects, services, entities, repositories, etc. Você é suposto de orientar-se por domain concerns em detrimento a infrastructure concerns. 👍
Minha resposta já ficou enorme e eu nem abordei tudo o que queria... 😆 Pra não prolongar muito eu recomendo fortemente estar vendo o capítulo de modularização do livro Domain-Driven Design in PHP. Tem ótimos exemplos de como cada camada é estruturada dentro dos bounded contexts.
Extraindo aqui parte da estrutura abordada no livro:
- src/
- Billing/
- Application/
- PlaceAnOrder/
- PlaceAnOrder.php (application service)
- PlaceAnOrderRequest.php (request DTO)
- PlaceAnOrderResponse.php (response DTO)
- Domain/
- Model/
- Bill/ (domain aggregate)
- Bill.php (domain entity/aggregate root)
- BillLine.php (domain entity ou value object)
- BillLineWasAdded.php (domain event)
- BillRepository.php (repository contract)
- BillWasCreated.php (domain event)
- Order/ (domain aggregate)
- Order.php (domain entity/aggregate root)
- OrderLine.php (domain entity ou value object)
- OrderLineWasAdded.php (domain event)
- OrderRepository.php (repository contract)
- OrderWasCreated.php (domain event)
- Waybill/ (domain aggregate)
- Waybill.php (domain entity/aggregate root)
- WaybillLine.php (domain entity ou value object)
- WaybillLineWasAdded.php (domain event)
- WaybillRepository.php (repository contract)
- WaybillWasGenerated.php (domain event)
- Infrastructure/
- Delivery/
- API/
- Laravel/
- Console/
- Symfony/
- Web/
- Silex/
- Slim/
- Domain/
- Model/
- Bill/
- DoctrineBillRepository.php (Doctrine repository implementation)
- InMemoryBillRepository.php (In-memory repository implementation)
- RedisBillRepository.php (Redis repository implementation)
- Order/
- DoctrineOrderRepository.php (Doctrine repository implementation)
- InMemoryOrderRepository.php (In-memory repository implementation)
- RedisOrderRepository.php (Redis repository implementation)
- Waybill/
- DoctrineWaybillRepository.php (Doctrine repository implementation)
- InMemoryWaybillRepository.php (In-memory repository implementation)
- RedisWaybillRepository.php (Redis repository implementation)
- Logging/
- Messaging/
- Persistence/
- Doctrine/
- BaseDoctrineRepository.php (abstract Doctrine repository)
- EntityManagerFactory.php
- Mapping/
- ... (Doctrine mapping files *.dcm.yml)
O Sean Mumford (thepsion5) aborda alguns exemplos do Hexagonal no fórum do Laracasts aqui e aqui. O exemplo dele é relevante porque é o Hexagonal aplicado ao Laravel, lá ele demonstra como subdivide os componentes do skeleton inicial numa abordagem menos purista do Hexagonal.
É um ótimo exemplo de como você pode adotar uma arquitetura mais robusta sem desacoplar muito o framework dela. Observe que para aplicações pequenas como a que ele abordou você pode inclusive não se orientar à bounded contexts e considerar que tudo seja uma aplicação só - creio ser uma estruturação perfeitamente válida. No caso do DDD a única diferença no exemplo dele é que os namespaces Http
e Cli
estariam dentro da camada de infraestrutura (os tais delivery mechanisms). 👍
Alguns outros exemplos mais genéricos podem ser vistos no dddinphp/ddd, dddinphp/last-wishes, yuloh/quicksilver (tem uma aplicação Laravel separada: yuloh/l5-quicksilver), codeliner/php-ddd-cargo-sample, patrikfr/dddsample e VaughnVernon/IDDD_Samples (esses dois últimos são em Java mas são bem completos e se baseiam respectivamente no livro do Eric Evans e no livro do Vaughn Vernon).
Eu deixei o padrão arquitetural de projeto CQRS intencionalmente de fora para não complicar o exemplo abordado acima, mas ele também pode ser usado na estrutura DDD/Hexagonal. 👍
Eu não devo ter respondido a pergunta diretamente mas espero que os pontos abordados aqui te permitam ir mais além nesse universo fabuloso de projeto orientado ao domínio. 😄
Abraços! o/
PS: Se falei alguma besteira desde já peço perdão e que alguém me corrija, eu comprei o Big Blue Book recentemente mas ainda não li. 🤣
Vale ressaltar que o @hernandev sempre deixou claro que a estrutura dele é baseada em alguns conceitos DDD e não é uma estrutura DDD. E respondendo a pergunta inicial, eu faria como exposto pelo @zabaala
Justamente por isso que se faz relevante deixar claro quais são exatamente os princípios que orientam o Domain-Driven Design, quais os objetivos de arquiteturas como o DDD em desacoplar as diferentes camadas entre si e porque não existe muita variação de estruturação quando você leva em consideração que arquiteturas como o DDD são orientadas aos conceitos de domínio e não à infraestrutura. 😄
@fraterblack obrigado pela ressalva.
@omarkdev de fato, essa nossa estrutura é inspirada em DDD.
Eu considero ela o máximo coerente a se trabalhar, com Laravel.
Pra fazer DDD "real", no meu ponto de vista, não seria nem muito interessante o uso das estruturas skeleton dos frameworks, pois nenhuma é feita com DDD em mente.
Estou criando uma aplicação em Laravel, que terá uma API e uma Dashboard. Seguindo a estrutura de pastas do DDD que o @hernandev fala nas series Laravel Hardcore, eu usava assim:
Porém me veio a seguinte questão, vocês acham válido ter uma estrutura assim:
app:
Ou colocar tudo dentro dos Domains mesmo? Vocês tem alguma sugestão que fique agradável?
Obrigado.