Хотелось бы спросить мнение у форумчан (часто встречаю много полезного на этом форуме)
1) Такой пример - клиент оставляет отзыв на сайте.
Код: Выделить всё
namespace domain/client;
use domain/review/Review;
use domain/review/ReviewDto;
class Client implements ManageReviewInterface, AuthorInterface
{
private $name;
private $surname;
public function createReview($reviewId, $placeId, ReviewDto $reviewData)
{
return new Review($reviewId, $placeId, $reviewData, $this)
}
public function authorFullName()
{
return $this->name . ' ' . $this->surname;
}
}
-----------------------
namespace domain/client;
use domain/review/ReviewDto;
interface ManageReviewInterface
{
public function createReview($placeId, ReviewDto $reviewData);
}
-----------------------
namespace domain/client;
interface AuthorInterface
{
public function authorFullName();
}
------------------------
namespace domain/review;
class Review
{
public finction __construct($reviewId, $placeId, ReviewDto $reviewData, AuthorInterface $author)
{
$this->reviewId= (int)$reviewId;
$this->placeId = (int)$placeId;
$this->message = $reviewData->message;
$this->authorName = $author->authorFullName();
}
}
namespace domain/review;
class ReviewDto
{
public $message;
public $rating;
}
1.1) На сколько корректно когда entity из одного модуля создает entity из другого модуля? По DDD так вроде и должно быть, код получается красивый ($client->createReview()), в реальной жизни так и происходит. Здесь есть одно НО - тем самым делаем coupling, хотя опять же связность однонаправленная, что допустимо.
1.2) Используете ли Вы DTO вот таким вот образом? Уточню, в реальности в ReviewDto 10 характеристик, часть из которых не обязательны.
Смущает то что типично DTO используется между Application и Presentation.
1.3) Местонахождения интерфейсов.
AuthorInterface может реализовывать помимо клиента менеджер или партнер или еще какие-то сущности в системе. 1 вариант - такие интерфейсы лучше класть в папку domain/contracts. 2 вариант - класть в папку к каждой сущности (копипаст)
ManageReviewInterface возможно должен быть в модуле review, тогда связность 2-х модулей еще возрастает или также в папке domain/contacts, потому что много кто может управлять созданием и редактирование отзывов.
2) Mapper для ValueObject (дальше VO для краткости).
У нас сделано следующим образом - Repository достает данные и отдает mapper, который с помощью расширения от SamDark - hydrator собирает сущности.Были ли у вас кейсы, когда тебе нужно будет собрать вместо сущности VO?
3) Mapper или Repository знает о domain.
Много примеров типа $mapper->populate('domain/client/Client', $data)
Тем самым инфраструктура (persistence) начинает что-то знать о слое выше (namespace Entity), что в какой-то мере нарушает принцип слоенной архитектуры.
Еще есть что-то вот такое, для агрегатов:
Код: Выделить всё
namespace infrastructure/persistence/mappers/custom;
use domain/clinet/PhoneNumber;
ClientMapper {
public function populate($data)
{
...
foreach ($data['phoneNumber'] as $phoneNumber) {
$client->phoneNumbers[] = new PhoneNumber($phoneNumber);
}
}
}
3.2) Собственно здесь еще раз возвращаемся ко 2 вопросу, возможно пользоваться mapper для VO и будет что-то типа:
Код: Выделить всё
$client->phoneNumbers[] = $phoneNumberMapper->populate($poneNumber);
4) Может ли Repository искать что-то кроме entity, возвращать массивы? Пример - данные для отчета. Или сохранять какую-то статистику, которая позже отсылается в Google Analytics. В этих задачах уже речь не идет о DDD и моделях.
4.1) У репозитория есть интерфейс, где он должен находится? (в application или рядом с реализацией)
5) NextIdentity. В Entity всегда должны быть соблюдены все инварианты и она должны быть валидна. База (MySQL) была спроектирована так, что отличительная особенность entity это Auto Increment Id.
Насколько корректно в транзакции вставлять в базу пустую строку в методе Repository->nextIdentity, чтобы получить и занять ID? Если не удалось создать объект, то транзакция откатывается и пустая строка из базы удаляется. Это пахнет жестким костылем, но в MySQL по-другому никак.
6) Есть ли такое понятие, что Domain слой должен быть максимально независим от других и при желании его можно перенести в другой проект? В реальной жизни сложно придумать такой пример, когда слой бизнеса можно перенести в другое приложение, пусть даже и бизнес схож по деятельности
7) Интефейсы инфраструктурных сервисов кладем на доменный уровень, чтобы не лишать себя возможности использовать их в Domain Service?
Пример TransactionMangerInterface лежать должен где-то на доменном уровне (domain/contacts/TransactionMangerInterface), а его реализация на инфраструктурном уровне (infrastructure/persistence/transaction/TransactionManger). Но в 90% мы его используем в Application Service