我正在考虑一个广告网站,用户可以登录,发布新的列表和搜索现有的列表.我将完全按照DDD原则将其作为我的第一个项目.我之前从未在Symfony做过任何DDD.
以下是我对此的看法.你能否告诉我这是否正确,并建议更好的方法?
我可以看到两个域:用户和列表
搜索/显示/发布功能将存在于清单域中.在用户域中实时登录/注销.
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是一个好主意还是我应该在User和Listing包中存在持久性?
框架和DDD
你在这里做了一个错误的假设,即“我将使用Symfony框架以DDD-ish方式实现我的应用程序”.
不要这样做,Frameworks are just an implementation detail并为您的应用程序提供一种(更多)交付方法.我的意思是在Hexagonal Architecture的背景下申请.
如果从我们的某个上下文中查看以下示例,您会看到我们的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传递,因为严格意义上的六角架构它是一个应用程序到应用程序端口).我强烈建议你阅读DDD in PHP book,其中所有这些概念都是用PHP中的表达实例来解释的(假设你已经阅读了蓝皮书和红皮书,尽管这本书作为一个独立的,同时仍然提供参考).