ИМХО, но DDD очень сложен в 100% имплементации на любом из языков программирования. И мы, как разработчики, все равно будем ориентироваться на выбранную ORM или сериализацию, хотя бы с целью оптимизации времени на разработку. DDD сам по себе добавляет весьма заметный оверхед на разработку
Да про null я уже упомянул.
А EntryState у Owned Type — не думаю, что это может быть проблемой, поскольку VO в принципе не могут быть изменены.
Если мы хотим изменить VO/Owned Type, то мы должны создать новый VO. На примере Person и PersonalName это будет примерно так:
var firstName = new Name(model.FirstName);
var lastName = new Name(model.LastName);
person.PersonalName = new PersonalName(firstName, lastName);
Конечно, можно. Но не нужно.
Во первых, атрибуты — это такая себе штука в DDD. Чем меньше домен замусорен атрибутами (тем более ориентированными на инфраструктурный слой), тем лучше. Но в некоторых случаях без них не обойтись — нет идеальных ЯП, библиотек и ORM.
Во вторых — две мощнейшие ORM в .Net Core, EF Core и NHibernate, многое полезное для DDD умеют «из коробки». Хотя NHibernate в плане удобства для DDD явно выигрывает у EF Core, но EF Core активно развивается. И в этом плане я не считаю Value Conversions костылем. В обычном EF все гораздо хуже.
В третьих — нужно еще оценить затраты времени на создание такого proxy-слоя между доменом и ORM. Опять же, EF Core хорошо шагнул вперед и постоянно развивается, а NHibernate в этом плане давно все умеет без хитрых костылей
Не соглашусь с некоторыми моментам в статье. DDD — это не про приватные сеттеры и публичные методы с проверками.
Пример в статье очень легко привести в невалидное состояние — например, у Title может быть ограничение в БД, например, в 100 символов, Description — в 500.
Или по бизнес-правилам Title — это строка без переносов и спецсимволов, а в Description может быть html. ImageUrl тут легко может быть не валидным url, а чем угодно, и Price — отрицательным.
Используя String в качестве параметров — элементарно нарушить любые правила и привести модель в невалидное состояние, что для DDD — катастрофа.
Для того, чтобы этого избежать, модель должна работать только с Entity и ValueObject. Title, Description, ImageUrl и Price должны быть отдельными ValueObject, внутри которых есть собственные валидаторы, вызываемые в конструкторе и бросающие исключение, если что то не так. Неплохое решение для валидатора — публичные статические методы, пригодные для валидации значений извне, например полей dto.
А уже все Entity, в нашем случае Book, состоят исключительно из других ValueObject и связанных Entity, которые передаются через конструктор. Таким образом мы в принципе не сможем создать неправильную Entity, и стают ненужными большинство валидаторов в конструкторах и методах Entity. ValueObject'ы же вполне могут быть переиспользованы в любых других местах.
Так же совсем не обязательно сеттеры для всех свойств Entity делать приватными. Если бизнес-правила допускают, что свойство может просто изменяться без каких либо других ограничений, то какой смысл делать сеттер приватным при использовании ValueObject?
Тем более, что EF Core с версии 2.1 позволяет задавать собственные «Value conversions» для таких объектов. Но тут есть некоторые ограничения, главное из которых — если такой ValueObject попадает в expression tree, например — .Where(), то в этом случае из БД загрузяться все обьекты и только потом отфильтруются. Но это можно легко обойти. NHibernate в этом плане гораздо лучше подходит для DDD.
Минус данного подхода — заметно увеличивается время разработки, огромный плюс — модель никаким образом не станет невалидной.
Ну и про паттерны Repository и UnitOfWork. Конечно, EF Core это все реализует из коробки.
Но по DDD, Persistence — это отдельный слой на Инфраструктурном уровне, который скрывает в себе все логику хранения данных. Остальные слои ничего про это не должны знать. Если, условно, Persistense убрать из проекта, то остальной код этого даже не должен заметить. Но если мы напрямую передаем контекст БД в Presentation layer или домен — мы тут же жестко связываем эти слои с Persistense и EF Core и раскрываем детали имплементации. А жесткая связь между разными слоями или нюансами реализации в DDD — это провал. Мало того, передавая контекст в остальные слои, мы позволяем там очень много вольностей. По этому в домене должны быть интерфейсы репозиториев, а в Persistense — их реализация ровно в том объеме, который нужен остальным слоям.
DDD вносит очень большой оверхед в разработку, но, как по мне — оно того точно стоит.
Having the domain model separated from the persistence model
ИМХО, но DDD очень сложен в 100% имплементации на любом из языков программирования. И мы, как разработчики, все равно будем ориентироваться на выбранную ORM или сериализацию, хотя бы с целью оптимизации времени на разработку. DDD сам по себе добавляет весьма заметный оверхед на разработку
А EntryState у Owned Type — не думаю, что это может быть проблемой, поскольку VO в принципе не могут быть изменены.
Если мы хотим изменить VO/Owned Type, то мы должны создать новый VO. На примере Person и PersonalName это будет примерно так:
Никаких проблем или костылей я тут не вижу
Во первых, атрибуты — это такая себе штука в DDD. Чем меньше домен замусорен атрибутами (тем более ориентированными на инфраструктурный слой), тем лучше. Но в некоторых случаях без них не обойтись — нет идеальных ЯП, библиотек и ORM.
Во вторых — две мощнейшие ORM в .Net Core, EF Core и NHibernate, многое полезное для DDD умеют «из коробки». Хотя NHibernate в плане удобства для DDD явно выигрывает у EF Core, но EF Core активно развивается. И в этом плане я не считаю Value Conversions костылем. В обычном EF все гораздо хуже.
В третьих — нужно еще оценить затраты времени на создание такого proxy-слоя между доменом и ORM. Опять же, EF Core хорошо шагнул вперед и постоянно развивается, а NHibernate в этом плане давно все умеет без хитрых костылей
Пример в статье очень легко привести в невалидное состояние — например, у Title может быть ограничение в БД, например, в 100 символов, Description — в 500.
Или по бизнес-правилам Title — это строка без переносов и спецсимволов, а в Description может быть html. ImageUrl тут легко может быть не валидным url, а чем угодно, и Price — отрицательным.
Используя String в качестве параметров — элементарно нарушить любые правила и привести модель в невалидное состояние, что для DDD — катастрофа.
Для того, чтобы этого избежать, модель должна работать только с Entity и ValueObject. Title, Description, ImageUrl и Price должны быть отдельными ValueObject, внутри которых есть собственные валидаторы, вызываемые в конструкторе и бросающие исключение, если что то не так. Неплохое решение для валидатора — публичные статические методы, пригодные для валидации значений извне, например полей dto.
А уже все Entity, в нашем случае Book, состоят исключительно из других ValueObject и связанных Entity, которые передаются через конструктор. Таким образом мы в принципе не сможем создать неправильную Entity, и стают ненужными большинство валидаторов в конструкторах и методах Entity. ValueObject'ы же вполне могут быть переиспользованы в любых других местах.
Так же совсем не обязательно сеттеры для всех свойств Entity делать приватными. Если бизнес-правила допускают, что свойство может просто изменяться без каких либо других ограничений, то какой смысл делать сеттер приватным при использовании ValueObject?
Тем более, что EF Core с версии 2.1 позволяет задавать собственные «Value conversions» для таких объектов. Но тут есть некоторые ограничения, главное из которых — если такой ValueObject попадает в expression tree, например — .Where(), то в этом случае из БД загрузяться все обьекты и только потом отфильтруются. Но это можно легко обойти. NHibernate в этом плане гораздо лучше подходит для DDD.
Минус данного подхода — заметно увеличивается время разработки, огромный плюс — модель никаким образом не станет невалидной.
Ну и про паттерны Repository и UnitOfWork. Конечно, EF Core это все реализует из коробки.
Но по DDD, Persistence — это отдельный слой на Инфраструктурном уровне, который скрывает в себе все логику хранения данных. Остальные слои ничего про это не должны знать. Если, условно, Persistense убрать из проекта, то остальной код этого даже не должен заметить. Но если мы напрямую передаем контекст БД в Presentation layer или домен — мы тут же жестко связываем эти слои с Persistense и EF Core и раскрываем детали имплементации. А жесткая связь между разными слоями или нюансами реализации в DDD — это провал. Мало того, передавая контекст в остальные слои, мы позволяем там очень много вольностей. По этому в домене должны быть интерфейсы репозиториев, а в Persistense — их реализация ровно в том объеме, который нужен остальным слоям.
DDD вносит очень большой оверхед в разработку, но, как по мне — оно того точно стоит.
Иногда очень хотелось что-либо уточнить или задать вопрос автору топика (или полезного комментария), если что-то непонятно.