DDD в структуре папок SF3
Я думаю через рекламный сайт, где пользователи могут войти в систему, размещать новые списки и искать существующие. Я собираюсь сделать этот мой первый проект полностью следуя принципам DDD. Я никогда раньше не делал DDD в Symfony.
Ниже приведены мои мысли об этом. Не могли бы вы сказать мне, если это правильно и советы о лучших способов?
Я вижу два домена: User и Listing
функции поиска/отображения/публикации будут жить в листинге домен. Вход / выход в реальном времени в домене пользователя.
SF3 пример структуры каталога -
app/
ListingBundle/
src/
Listing.php
SearchService.php
ListingRepositoryInterface.php
Controller/
public/
ListingController.php
protected/
ListingController.php
Resource/
view/
public/
detail.twig.html
protected/
edit.twig.html
UserBundle/
src/
User.php
AuthService.php
UserRepositoryInterface.php
Controller/
public/
UserController.php
protected/
UserController.php
Resource/
view/
public/
login.twig.html
protected/
dashboard.twig.html
PersistenceBundle
src/
UserRepository.php
ListingRepository.php
мои основные вопросы:
- эта структура правильная?
- хорошо ли иметь отдельные защищенные и общедоступные контроллеры с одинаковым именем?
- куда идут вещи, как страница на пользовательской части веб-сайта, показывающая последние объявления, опубликованные пользователем? Где граница между этими двумя Домены?
- является ли PersistenceBundle хорошей идеей или я должен иметь persistence live внутри пользователя и листинга?
2 ответов
фреймворки & DDD
Вы делаете неправильное предположение здесь, которое "Я собираюсь использовать Symfony framework для реализации моего приложения в DDD-ish способом".
не делай этого, рамки-это просто деталь реализации и представить один (или более) методов доставки приложение. И я имею в виду применение здесь, в контексте шестиугольная Архитектура.
если вы посмотрите на следующий пример из одного из наших контекстов, вы увидите, что наш ApiClient
контекст содержит три слоя (структура каталогов верхнего уровня). приложение (содержит службы прецедентов),домен (содержит модели и поведение) и инфраструктура (содержит проблемы инфраструктуры, такие как постоянство и доставка). Я сосредоточился на интеграции Symfony и постоянстве здесь, так как это о чем был первоначальный вопрос OP:
src/ApiClient
├── Application
│ ├── ApiClient
│ │ ├── CreateApiClient
│ │ ├── DisableApiClient
│ │ ├── EnableApiClient
│ │ ├── GetApiClient
│ │ ├── ListApiClient
│ │ ├── RemoveApiClient
│ │ └── ChangeApiClientDetails
│ ├── ClientIpAddress
│ │ ├── BlackListClientIpAddress
│ │ ├── CreateClientIpAddress
│ │ ├── ListByApiClientId
│ │ ├── ListClientIpAddresses
│ │ └── WhiteListClientIpAddress
│ └── InternalContactPerson
│ ├── CreateInternalContactPerson
│ ├── GetInternalContactPerson
│ ├── GetByApiClientId
│ ├── ListContacts
│ ├── ReassignApiClient
│ └── Remove
├── Domain
│ └── Model
│ ├── ApiClient
│ ├── ClientIpAddress
│ └── InternalContactPerson
└── Infrastructure
├── Delivery
│ └── Http
│ └── SymfonyBundle
│ ├── Controller
│ │ ├── ApiClientController.php
│ │ ├── InternalContactController.php
│ │ └── IpAddressController.php
│ ├── DependencyInjection
│ │ ├── Compiler
│ │ │ ├── EntityManagerPass.php
│ │ │ └── RouterPass.php
│ │ ├── Configuration.php
│ │ ├── MetadataLoader
│ │ │ ├── Adapter
│ │ │ │ ├── HateoasSerializerAdapter.php
│ │ │ │ └── JMSSerializerBuilderAdapter.php
│ │ │ ├── Exception
│ │ │ │ ├── AmbiguousNamespacePathException.php
│ │ │ │ ├── EmptyMetadataDirectoryException.php
│ │ │ │ ├── FileException.php
│ │ │ │ ├── MalformedNamespaceException.php
│ │ │ │ └── MetadataLoadException.php
│ │ │ ├── FileMetadataLoader.php
│ │ │ ├── MetadataAware.php
│ │ │ └── MetadataLoaderInterface.php
│ │ └── MFBApiClientExtension.php
│ ├── DTO
│ │ └── ApiClient
│ │ └── ChangeInternalContact
│ │ ├── ChangeInternalContactRequest.php
│ │ └── ChangeInternalContactResponse.php
│ ├── MFBApiClientBundle.php
│ ├── Resources
│ │ ├── config
│ │ │ ├── domain_services.yml
│ │ │ ├── metadata_loader.yml
│ │ │ ├── routing.yml
│ │ │ └── services.yml
│ │ ├── hateoas
│ │ │ └── ApiClient
│ │ │ ├── Application
│ │ │ │ ├── ApiClient
│ │ │ │ │ ├── CreateApiClient
│ │ │ │ │ │ └── CreateApiClientResponse.yml
│ │ │ │ │ └── ListApiClient
│ │ │ │ │ └── ListApiClientResponse.yml
│ │ │ │ ├── ClientIpAddress
│ │ │ │ │ ├── CreateClientIpAddress
│ │ │ │ │ │ └── CreateClientIpAddressResponse.yml
│ │ │ │ │ ├── ListByApiClientId
│ │ │ │ │ │ └── ListByApiClientIdResponse.yml
│ │ │ │ │ └── ListClientIpAddresses
│ │ │ │ │ └── ListClientIpAddressesResponse.yml
│ │ │ │ └── InternalContactPerson
│ │ │ │ ├── Create
│ │ │ │ │ └── CreateResponse.yml
│ │ │ │ └── List
│ │ │ │ └── ListResponse.yml
│ │ │ └── Domain
│ │ │ ├── ApiClient
│ │ │ │ └── ApiClient.yml
│ │ │ ├── ClientIpAddress
│ │ │ │ └── ClientIpAddress.yml
│ │ │ └── InternalContactPerson
│ │ │ └── InternalContactPerson.yml
│ │ └── serializer
│ │ ├── ApiClient
│ │ │ ├── Application
│ │ │ │ ├── ApiClient
│ │ │ │ │ ├── CreateApiClient
│ │ │ │ │ │ ├── ContactPersonRequest.yml
│ │ │ │ │ │ ├── CreateApiClientRequest.yml
│ │ │ │ │ │ └── CreateApiClientResponse.yml
│ │ │ │ │ └── GetApiClient
│ │ │ │ │ └── GetApiClientResponse.yml
│ │ │ │ ├── ClientIpAddress
│ │ │ │ │ └── CreateClientIpAddress
│ │ │ │ │ ├── CreateClientIpAddressRequest.yml
│ │ │ │ │ └── CreateClientIpAddressResponse.yml
│ │ │ │ └── InternalContactPerson
│ │ │ │ ├── Create
│ │ │ │ │ ├── CreateRequest.yml
│ │ │ │ │ └── CreateResponse.yml
│ │ │ │ ├── Get
│ │ │ │ │ └── GetResponse.yml
│ │ │ │ ├── List
│ │ │ │ │ └── ListResponse.yml
│ │ │ │ └── ReassignApiClient
│ │ │ │ └── ReassignApiClientRequest.yml
│ │ │ └── Domain
│ │ │ ├── ApiClient
│ │ │ │ ├── ApiClient.yml
│ │ │ │ └── ContactPerson.yml
│ │ │ ├── ClientIpAddress
│ │ │ │ └── ClientIpAddress.yml
│ │ │ └── InternalContactPerson
│ │ │ └── InternalContactPerson.yml
│ │ └── Bundle
│ │ └── DTO
│ │ └── ApiClient
│ │ └── ChangeInternalContact
│ │ └── ChangeInternalContactRequest.yml
│ └── Service
│ └── Hateoas
│ └── UrlGenerator.php
└── Persistence
├── Doctrine
│ ├── ApiClient
│ │ ├── ApiClientRepository.php
│ │ └── mapping
│ │ ├── ApiClientId.orm.yml
│ │ ├── ApiClient.orm.yml
│ │ ├── CompanyName.orm.yml
│ │ ├── ContactEmail.orm.yml
│ │ ├── ContactList.orm.yml
│ │ ├── ContactName.orm.yml
│ │ ├── ContactPerson.orm.yml
│ │ ├── ContactPhone.orm.yml
│ │ └── ContractReference.orm.yml
│ ├── ClientIpAddress
│ │ ├── ClientIpAddressRepository.php
│ │ └── mapping
│ │ ├── ClientIpAddressId.orm.yml
│ │ ├── ClientIpAddress.orm.yml
│ │ └── IpAddress.orm.yml
│ └── InternalContactPerson
│ ├── InternalContactPersonRepository.php
│ └── mapping
│ ├── InternalContactPersonId.orm.yml
│ └── InternalContactPerson.orm.yml
└── InMemory
├── ApiClient
│ └── ApiClientRepository.php
├── ClientIpAddress
│ └── ClientIpAddressRepository.php
└── InternalContactPerson
└── InternalContactPersonRepository.php
94 directories, 145 files
довольно много файлов!
вы можете видеть, что я использую пакет как порт приложения (именование немного, хотя, это не должно быть Http
доставка, так как в строгом смысле слова Гексагональной Архитектуры это Порт App-To-App). Я настоятельно рекомендую вам прочитать DDD в PHP book где все эти понятия фактически объяснено выразительными примерами в PHP (предполагая, что вы уже прочитали синюю книгу и Красную книгу, хотя эта книга работает как отдельная, все еще делая ссылки).
структура папок для приложений DDD, построенных с помощью Symfony
Я второй ответ tPl0ch, но хотел бы предложить небольшой вариант структуры папок, который был полезен в нескольких проектах с Symfony, в которых я участвовал. Для вашего конкретного домена структура папок может выглядеть следующим образом:
app
Listing
Domain
Model
Listing.php
Repository
ListingRepository.php
Service
SearchService.php
Infrastructure
Repository
DoctrineListingRepository.php // or some other implementation
Resources
// symfony & doctrine config etc.
Service
ElasticSearchService.php // or some other implementation
ListingInfrastructureBundle.php
Presentation
Controller
ViewListingController.php // assuming this is the "public" part
EditListingController.php // assuming this is the "protected" part
Forms
ListingForm.php
Resources
// symfony config & views etc.
ListingPresentationBundle.php
User
// ...
Infrastructure
Service
AuthService.php
// ...
С помощью этой структуры папок вы разделяете различные слои лук архитектуры. Другое папки четко связывают границы и разрешенные зависимости между слоями. Я написал сообщение в блоге на DDD структуры папок с Symfony который подробно описывает подход.
Дополнительные Материалы:
кроме того, я также рекомендую взглянуть на следующие ресурсы:
- PHP DDD образец груза: PHP 7 версия образца груза, используемого в Eric Evans DDD книга
- Sylius: PHP-фреймворк электронной коммерции, построенный поверх Symfony с компонентной архитектурой
Я многому научился от понимания базы кода Sylius - это реальный проект и довольно огромный. Они имеют все виды тестов и приложили много усилий для доставки высококачественного кода.