Ну, я в статье несколько раз упоминал, что нет одного правильного способа. Просто потому что нет исследований, которые бы подтвердили, что одно правильнее другого. Поэтому смело можно брать тот вариант, который кажется сейчас правильнее. Потом все равно поток новых знаний заставит скорректировать взгляды.
По поводу вопроса:
Я не припомню, когда в последний раз нам требовалось мучиться с огромным количеством полей, которые бы могли меняться все без исключения. Что-то похожее решаем ломбоком с публичными сеттерами, если никакой валидации при изменении не требуется. Если требуется - это отдельный разговор.
Очень близко! На самом деле, основных вариантов два:
Самый простой, нет никакой валидации:
public class CreateOrderDTO
{
public String number;
public DateTime date;
// еще 100 500 полей
Order toOrder(){
return new Order(number, date, ...); //100 полей
}
// или...
Order toOrder(){
return Order.builder()
.number(number)
.date(date)
// 100 полей
.build();
}
// То же самое, но чуть понагляднее.
// В целом, я билдеры не рекомендую,
// потому что они порой неприятно
// привязываются к структуре классов
// и потом больно рефакторить.
}
Более сложный, необходима валидация. Тогда мы передаем логику валидации в фабричный метод класса, чтобы логика не утекала из домена и делаем конструктор приватным, чтобы нельзя было создать объект в невалидном состоянии.
public class CreateOrderDTO
{
public string number;
public DateTime date;
// еще 100 500 полей
Order toOrder(){
return Order.from(number, date, ...); //100 полей
}
}
public class Order
{
string number;
DateTime date;
// еще 100 500 полей
// тут private-метод, потому что нельзя допускать создание невалидного объекта
private Order(/* 100 500 полей*/)
{
}
public static Order from(String number, .../* 100 500 полей*/) {
if (! number.matches("[0-9]+")) {
throw new NotValidException("Номер не номер!");
// Ну или можно сделать иначе:
// https://martinfowler.com/eaaDev/Notification.html
// Но, вообще говоря, этой проверочке место
// в отдельном классе Number, который должен
// использоваться вместо строки.
}
return new Order(/* 100 500 полей*/)
}
}
Ну и есть еще варианты с полиморфным созданием, с фабриками, абстрактными фабриками и прочей шляпой под определенные задачи и проблемы. Я начал их рисовать и понял, что это уже вопросы отдельных паттернов.
Потащим ли мы вызов из одного адаптера в другой через сервисный слой?
Ответ: да, обычно мы так делаем, но это не обязательно. Делаем, потому что недорого, но при этом добавляет абстракции.
Как правильно валидировать уникальность наименования документа?
Ответ: уникальность наименования - это бизнес-правило, оно должно быть в домене. Если документов немного, то идеальный способ реализации: загрузить все документы из базы в класс Documents, вызвать на нём метод add, который провалидирует, возможно ли это добавление, по всем необходимым бизнес-правилам, в том числе и проверит уникальность. Делегировать проверку констрейнту в базе - значит, нарушить инкапсуляцию нашей модели: теперь часть бизнес логики в домене в модели, а часть - в хранилище в базе данных.
Универсально ли предложенное мной решение? Нет, если документов слишком много, оно будет работать долго и требовать много памяти. Поэтому есть DDD-трилемма. Из трёх свойств: инкапсуляции, изоляции и производительности вы можете выбрать только 2. Подробнее рекомендую посмотреть тут: https://youtu.be/JOy_SNK3qj4?si=WHt8iUkhT1y5zD8q
Либо конструктор, либо фабрика, либо фабричный метод. В зависимости от сложности модельного объекта. В простом случае конструктор. Если нужна валидация, то фабричный метод. Если есть полиморфизм, то абстрактная фабрика.
Сеттеры на доменных моделях, как правило, отсутствуют вовсе. Геттеры только по необходимости.
В нашей практике, маппинг дто на доменные объекты происходит в простом случае в коде дто. Мы просто делаем метод что-то вроде dto.toDomain(), внутри которого вызываются конструкторы или фабрики домена, и обратный статический Dto.from(). Ну только имена поприятнее.
И я бы на этом остановился, но поделюсь такой проблемой: тут есть нюанс с валидацией и тестированием. В нашем процессе мы делим автотесты на быстрые без запуска приложения и медленные на запущенном приложении. Сами быстрые, в свою очередь, делятся на тесты адаптера, где мы его тестируем с конкретной технологией, и тесты приложения, где мы проверяем именно логику приложения и домена через интерфейсы приложения.
Так вот, в описанном подходе, если есть какая-то валидация доменного объекта при создании (например, в фабрике), то, хоть логика этой валидации и лежит в домене, но тесты, как будто, приходится писать либо в адаптере, либо отдельно на фабрику. Либо, третий вариант: использовать, как вы сказали, промежуточный объект, который, в отличие от модели, может быть в невалидном состоянии - этакий builder из gof. Последнее решение рекомендует дядя Боб в своей Чистой Архитектуре.
Но, например, Алексей в своём Эргономичном коде не использует такого деления тестов и, как следствие, таких проблем не имеет. Но он говорит, что и гексагоналку почти не использует, что логично, ведь главный плюс гексагоналки проявляется только при таком делении тестов. Но зато у нас есть тест-сьют, который едет со скоростью 500 тестов за пару секунд.
Ответ на пассаж про то, что не вся индустрия так работает. Есть самые экономически эффективные способы работать, а есть спектр того, как компании работают в реальности. Компания может иметь ужасное айти, но шикарный продукт; плохое все, но хороший маркетинг. И там ещё сотни разных комбинаций, которые позволяют компаниям иметь неэффективные процессы.
Пассаж про разработчиков. Если нанимать, бог знает кого, то и проблемы с мотивацией и дисциплиной будут соответствующие. В статье чётко написано: не нанимайте людей, которым вы не будете доверять.
Единичные случаи - действительно слабый аргумент. Хорошим аргументом является по-научному крепое исследование. И вот тут я вас отсылаю в отчёты state of devops 2016 и 2017, где вопросу continuous integration и trunk based development посвящены соответствующие разделы. Почитайте, пожалуйста, там не много. Но связь с экономикой прямая: high performers типа той компании, ссылку на которую опубликовал наш коллега, имеют заметно более высокие шансы экономического успеха. И такие компании гораздо реже используют долгоживущие ветки или ветки вовсе.
Мне, кстати, интересно было бы узнать о результатах.
Уже третий год наша замечательно команда работает в таких процессах, всем нравится, добровольно покинувших команду пока нет.
Проблемы недоверия в команде нет. Работает, как единый механизм, а не как группа людей, работающих для строчки в резюме. Покрытие тестами полное, ответственность высочайшая, самодисциплина на высоте, атмосфера дружеская. Никакой бюрократии и принуждения тоже нет - наоборот, все члены команды ценятся, их интересы, пожелания, эмоции внимательно отслеживаются и учитываются.
Если интересно последить за успехами и неудачами нашей команды - добро пожаловать в https://t.me/RakovskyXP
Ну, я в статье несколько раз упоминал, что нет одного правильного способа. Просто потому что нет исследований, которые бы подтвердили, что одно правильнее другого. Поэтому смело можно брать тот вариант, который кажется сейчас правильнее. Потом все равно поток новых знаний заставит скорректировать взгляды.
По поводу вопроса:
Я не припомню, когда в последний раз нам требовалось мучиться с огромным количеством полей, которые бы могли меняться все без исключения. Что-то похожее решаем ломбоком с публичными сеттерами, если никакой валидации при изменении не требуется. Если требуется - это отдельный разговор.
Да, иногда мы так делаем, если лень писать сервис. Мы не очень догматичны в своей работе: если нам что-то не понравится - зарефачим, делов-то.
Да, сохранять весь набор документов списком. Паттерн называется Aggregate из оригинальной книжки DDD.
Очень близко! На самом деле, основных вариантов два:
Самый простой, нет никакой валидации:
Более сложный, необходима валидация. Тогда мы передаем логику валидации в фабричный метод класса, чтобы логика не утекала из домена и делаем конструктор приватным, чтобы нельзя было создать объект в невалидном состоянии.
Ну и есть еще варианты с полиморфным созданием, с фабриками, абстрактными фабриками и прочей шляпой под определенные задачи и проблемы. Я начал их рисовать и понял, что это уже вопросы отдельных паттернов.
Добрый день)
Это, на самом деле, 2 вопроса:
Потащим ли мы вызов из одного адаптера в другой через сервисный слой?
Ответ: да, обычно мы так делаем, но это не обязательно. Делаем, потому что недорого, но при этом добавляет абстракции.
Как правильно валидировать уникальность наименования документа?
Ответ: уникальность наименования - это бизнес-правило, оно должно быть в домене. Если документов немного, то идеальный способ реализации: загрузить все документы из базы в класс Documents, вызвать на нём метод add, который провалидирует, возможно ли это добавление, по всем необходимым бизнес-правилам, в том числе и проверит уникальность. Делегировать проверку констрейнту в базе - значит, нарушить инкапсуляцию нашей модели: теперь часть бизнес логики в домене в модели, а часть - в хранилище в базе данных.
Универсально ли предложенное мной решение? Нет, если документов слишком много, оно будет работать долго и требовать много памяти. Поэтому есть DDD-трилемма. Из трёх свойств: инкапсуляции, изоляции и производительности вы можете выбрать только 2. Подробнее рекомендую посмотреть тут: https://youtu.be/JOy_SNK3qj4?si=WHt8iUkhT1y5zD8q
Либо конструктор, либо фабрика, либо фабричный метод. В зависимости от сложности модельного объекта. В простом случае конструктор. Если нужна валидация, то фабричный метод. Если есть полиморфизм, то абстрактная фабрика.
Сеттеры на доменных моделях, как правило, отсутствуют вовсе. Геттеры только по необходимости.
Привет!
В нашей практике, маппинг дто на доменные объекты происходит в простом случае в коде дто. Мы просто делаем метод что-то вроде dto.toDomain(), внутри которого вызываются конструкторы или фабрики домена, и обратный статический Dto.from(). Ну только имена поприятнее.
И я бы на этом остановился, но поделюсь такой проблемой: тут есть нюанс с валидацией и тестированием. В нашем процессе мы делим автотесты на быстрые без запуска приложения и медленные на запущенном приложении. Сами быстрые, в свою очередь, делятся на тесты адаптера, где мы его тестируем с конкретной технологией, и тесты приложения, где мы проверяем именно логику приложения и домена через интерфейсы приложения.
Так вот, в описанном подходе, если есть какая-то валидация доменного объекта при создании (например, в фабрике), то, хоть логика этой валидации и лежит в домене, но тесты, как будто, приходится писать либо в адаптере, либо отдельно на фабрику. Либо, третий вариант: использовать, как вы сказали, промежуточный объект, который, в отличие от модели, может быть в невалидном состоянии - этакий builder из gof. Последнее решение рекомендует дядя Боб в своей Чистой Архитектуре.
Но, например, Алексей в своём Эргономичном коде не использует такого деления тестов и, как следствие, таких проблем не имеет. Но он говорит, что и гексагоналку почти не использует, что логично, ведь главный плюс гексагоналки проявляется только при таком делении тестов. Но зато у нас есть тест-сьют, который едет со скоростью 500 тестов за пару секунд.
100%
https://dora.dev/research/2016/
https://dora.dev/research/2017/
Информация есть в обоих отчётах.
Ответ на пассаж про то, что не вся индустрия так работает. Есть самые экономически эффективные способы работать, а есть спектр того, как компании работают в реальности. Компания может иметь ужасное айти, но шикарный продукт; плохое все, но хороший маркетинг. И там ещё сотни разных комбинаций, которые позволяют компаниям иметь неэффективные процессы.
Пассаж про разработчиков. Если нанимать, бог знает кого, то и проблемы с мотивацией и дисциплиной будут соответствующие. В статье чётко написано: не нанимайте людей, которым вы не будете доверять.
Спасибо за положительный комментарий)
Единичные случаи - действительно слабый аргумент. Хорошим аргументом является по-научному крепое исследование. И вот тут я вас отсылаю в отчёты state of devops 2016 и 2017, где вопросу continuous integration и trunk based development посвящены соответствующие разделы. Почитайте, пожалуйста, там не много. Но связь с экономикой прямая: high performers типа той компании, ссылку на которую опубликовал наш коллега, имеют заметно более высокие шансы экономического успеха. И такие компании гораздо реже используют долгоживущие ветки или ветки вовсе.
Все верно!
Буду знать, спасибо. Как у вас там с тестами?
Интересное место. Что за компания?
Уже третий год наша замечательно команда работает в таких процессах, всем нравится, добровольно покинувших команду пока нет.
Проблемы недоверия в команде нет. Работает, как единый механизм, а не как группа людей, работающих для строчки в резюме. Покрытие тестами полное, ответственность высочайшая, самодисциплина на высоте, атмосфера дружеская. Никакой бюрократии и принуждения тоже нет - наоборот, все члены команды ценятся, их интересы, пожелания, эмоции внимательно отслеживаются и учитываются.
Если интересно последить за успехами и неудачами нашей команды - добро пожаловать в https://t.me/RakovskyXP
Миш, рад видеть тебя тут! Спасибо за добрые слова)
Вот это другой разговор! 💪
Так и есть, асинхронный код ревью - тоже вполне себе практика.
В точку!
Спасибо большое за комментарий, это так приятно)