laravelbrasil / forum

Ama Laravel? Torne se um Jedi e Ajude outros Padawans
GNU General Public License v3.0
252 stars 13 forks source link

Arquitetura de pastas em DDD #58

Open omarkdev opened 7 years ago

omarkdev commented 7 years ago

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:

image

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.

zabaala commented 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

paulofreitas commented 7 years ago

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. 🤣

fraterblack commented 7 years ago

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

paulofreitas commented 7 years ago

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. 😄

hernandev commented 7 years ago

@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.