Всем привет!
Сегодня хочу поднять вопрос анемичной доменной модели. Давно холиварные вопросы не поднимал)
Одним из первых это понятие ввел Мартин Фаулер, см. https://martinfowler.com/bliki/AnemicDomainModel.html
Анемичной модели противопоставляется т.наз. "богатая" или, как я бы ее назвал, полноценная доменная модель.
В чем разница?
В анемичной доменной модели объекты по сути аналогичны DTO, их можно реализовать в виде Java records или Kotlin value object.
В полноценной - в объектах кроме данных есть бизнес-логика.
Вот аргументы сторонников полноценной модели:
https://www.baeldung.com/java-anemic-vs-rich-domain-objects
https://www.youtube.com/watch?v=lfdAwl3-X_c - особенно рекомендую, Егор как всегда подходит к теме "с огоньком")
Вот - сторонников анемичной модели: https://habr.com/ru/articles/346016/
Я в этом споре все же склоняюсь к полноценной модели.
Т.к. доменный объект - это не DTO, он должен содержать нечто большее. А именно - бизнес-логику. А вынося логику в отдельные сервисы легко так сильно ее размазать по коду, что это приведет к дублированию и ухудшению читаемости.
Более того: если модель становится анемичной - это может быть признаком, что модель не нужна, сервис является простым и можно вполне себе работать с DTO.
Особенно актуальна "богатая" доменная модель, если вы пытаетесь следовать практике DDD - Domain Driven Design. Собственно, ее автор, Эрик Эванс, сторонник этого подхода.
Да и есть смотреть определение класса в ООП - это не просто хранилище данных. Объект - это отражение сущности из реального мира. А в реальном мире объекты - кот, автомобиль, заказ - являются не только набором характеристик, но и могут что-то делать)
Но есть один нюанс.
Если логика не требует данных, не содержащихся в объекте - ей место в этом объекте.
Если требует данных извне, но не вызывает методы внешних объектов - аналогично.
А вот дальше простые правила заканчиваются и решения от сервиса к сервису могут отличаться. Как минимум стоит принять во внимание принцип SOLID, удобство переиспользования кода и удобство тестирования.
Да, и точно не стоит запихивать в модель инфраструктурный код - деление приложения на уровни никто не отменял.
Идея в том, что не надо боятся добавлять методы в объекты доменной модели. Более того, при проектировании стоит изначально думать о том, какими будут эти объекты.
Что касается нарушения принципов S и I из SOLID.
Не надо относится к этим принципам догматично. Если так делать - то у нас в итоге будет куча DTO с парой полей, куча интерфейсов с одним методом и взрыв мозга у людей, которые попытаются вникнуть в этом код с нуля) Важен баланс.
#anemic-vs-rich-domain-objects #holy_war #solid #oop
Сегодня хочу поднять вопрос анемичной доменной модели. Давно холиварные вопросы не поднимал)
Одним из первых это понятие ввел Мартин Фаулер, см. https://martinfowler.com/bliki/AnemicDomainModel.html
Анемичной модели противопоставляется т.наз. "богатая" или, как я бы ее назвал, полноценная доменная модель.
В чем разница?
В анемичной доменной модели объекты по сути аналогичны DTO, их можно реализовать в виде Java records или Kotlin value object.
В полноценной - в объектах кроме данных есть бизнес-логика.
Вот аргументы сторонников полноценной модели:
https://www.baeldung.com/java-anemic-vs-rich-domain-objects
https://www.youtube.com/watch?v=lfdAwl3-X_c - особенно рекомендую, Егор как всегда подходит к теме "с огоньком")
Вот - сторонников анемичной модели: https://habr.com/ru/articles/346016/
Я в этом споре все же склоняюсь к полноценной модели.
Т.к. доменный объект - это не DTO, он должен содержать нечто большее. А именно - бизнес-логику. А вынося логику в отдельные сервисы легко так сильно ее размазать по коду, что это приведет к дублированию и ухудшению читаемости.
Более того: если модель становится анемичной - это может быть признаком, что модель не нужна, сервис является простым и можно вполне себе работать с DTO.
Особенно актуальна "богатая" доменная модель, если вы пытаетесь следовать практике DDD - Domain Driven Design. Собственно, ее автор, Эрик Эванс, сторонник этого подхода.
Да и есть смотреть определение класса в ООП - это не просто хранилище данных. Объект - это отражение сущности из реального мира. А в реальном мире объекты - кот, автомобиль, заказ - являются не только набором характеристик, но и могут что-то делать)
Но есть один нюанс.
Если логика не требует данных, не содержащихся в объекте - ей место в этом объекте.
Если требует данных извне, но не вызывает методы внешних объектов - аналогично.
А вот дальше простые правила заканчиваются и решения от сервиса к сервису могут отличаться. Как минимум стоит принять во внимание принцип SOLID, удобство переиспользования кода и удобство тестирования.
Да, и точно не стоит запихивать в модель инфраструктурный код - деление приложения на уровни никто не отменял.
Идея в том, что не надо боятся добавлять методы в объекты доменной модели. Более того, при проектировании стоит изначально думать о том, какими будут эти объекты.
Что касается нарушения принципов S и I из SOLID.
Не надо относится к этим принципам догматично. Если так делать - то у нас в итоге будет куча DTO с парой полей, куча интерфейсов с одним методом и взрыв мозга у людей, которые попытаются вникнуть в этом код с нуля) Важен баланс.
#anemic-vs-rich-domain-objects #holy_war #solid #oop
martinfowler.com
bliki: Anemic Domain Model
If you use an object-oriented domain model, and you don't put behavior in your objects, you're missing out on most of the benefits of that pattern.
Всем привет!
Что включает в себя понятие объекта?
1) объект может иметь данные
2) данные объекта можно менять
3) объект может иметь поведение - методы
4) от класса объекта можно наследоваться
5) объектов одного класса может быть несколько
6) каждый объект уникален - находится в heap по своему адресу. Т.е. имеет identity
Можно ли отобрать у объекта что-то из вышеперечисленного в Java?
1) final class - запрещаем наследование
2) sealed class - если полный запрет нам не подходит, то ограничиваем наследование https://habr.com/ru/companies/otus/articles/720044/
3) final поля - запрещаем менять данные класса после создания объекта - иммутабельность
4) records - запрещаем методы и изменение полей https://habr.com/ru/articles/487308/ Основое применение - DTO, где есть только данные и их нежелательно менять.
5) lambda - можно рассматривать как объект-функцию без данных, хотя технически это не так - JVM под капотом транслирует ее в статический метод https://javadungeon.wordpress.com/2017/08/02/java-8-lambdas-under-the-hood/
6) object в Kotlin - синлетона на уровне языка в Java нет, но такую конструкцию сделали в Kotlin
7) value class - еще не реализованная в Java концепция, которая по сути добавляет в язык что-то среднее между примитивным типом и объектом. Т.е. у нас будет объект, но в плане занимаемой памяти он будет почти также эффективен, как и примитив. Что важно: т.к. это объект - его можно будет использовать в generics. Ура-ура!. Детали: https://www.baeldung.com/java-valhalla-project Но ничего не бывает бесплатно - у такого объекта не будет identity, т.е. два объекта с одинаковыми полями считаются одинаковыми. Также, как это сейчас происходит с примитивными типами. Да, для объектов это уже сейчас можно реализовать на уровне кода. А тут идея сделать тоже самое на уровне JVM. Т.е. JVM может (обязана?) оставить один экземпляр для всех одинаковых объектов. Причем так, как это сделано сейчас со строками - вариант не очень, т.к. остается "дыра" с конструктором, который по определению всегда создает новый объект. Т.е. для value class нельзя использовать публичный конструктор, нужен какой-то фабричный метод - как элемент языка. Примеры, когда это полезно: те же классы-обвертки для чисел или географические объекты.
#java #oop #kotlin
Что включает в себя понятие объекта?
1) объект может иметь данные
2) данные объекта можно менять
3) объект может иметь поведение - методы
4) от класса объекта можно наследоваться
5) объектов одного класса может быть несколько
6) каждый объект уникален - находится в heap по своему адресу. Т.е. имеет identity
Можно ли отобрать у объекта что-то из вышеперечисленного в Java?
1) final class - запрещаем наследование
2) sealed class - если полный запрет нам не подходит, то ограничиваем наследование https://habr.com/ru/companies/otus/articles/720044/
3) final поля - запрещаем менять данные класса после создания объекта - иммутабельность
4) records - запрещаем методы и изменение полей https://habr.com/ru/articles/487308/ Основое применение - DTO, где есть только данные и их нежелательно менять.
5) lambda - можно рассматривать как объект-функцию без данных, хотя технически это не так - JVM под капотом транслирует ее в статический метод https://javadungeon.wordpress.com/2017/08/02/java-8-lambdas-under-the-hood/
6) object в Kotlin - синлетона на уровне языка в Java нет, но такую конструкцию сделали в Kotlin
7) value class - еще не реализованная в Java концепция, которая по сути добавляет в язык что-то среднее между примитивным типом и объектом. Т.е. у нас будет объект, но в плане занимаемой памяти он будет почти также эффективен, как и примитив. Что важно: т.к. это объект - его можно будет использовать в generics. Ура-ура!. Детали: https://www.baeldung.com/java-valhalla-project Но ничего не бывает бесплатно - у такого объекта не будет identity, т.е. два объекта с одинаковыми полями считаются одинаковыми. Также, как это сейчас происходит с примитивными типами. Да, для объектов это уже сейчас можно реализовать на уровне кода. А тут идея сделать тоже самое на уровне JVM. Т.е. JVM может (обязана?) оставить один экземпляр для всех одинаковых объектов. Причем так, как это сделано сейчас со строками - вариант не очень, т.к. остается "дыра" с конструктором, который по определению всегда создает новый объект. Т.е. для value class нельзя использовать публичный конструктор, нужен какой-то фабричный метод - как элемент языка. Примеры, когда это полезно: те же классы-обвертки для чисел или географические объекты.
#java #oop #kotlin
Хабр
Sealed классы Java в действии: создание надежных и безопасных приложений
Sealed (закрытые или запечатанные) классы были представлены в Java 15 в качестве способа ограничить иерархию наследования класса или интерфейса. Sealed класс...
Всем привет!
Является ли ООП - объекто-ориентированное программирование - чем-то плохим? Ответ - ну нет. Благодаря объектам мы можем воспроизвести в коде реальные бизнес-объекты, а это стирает барьеры между заказчиком, аналитиком и разработчиком. Конечно, есть альтернативные подходы, например, функциональный. Или если посмотреть в другую сторону - декларативный. Но ООП жил, жив и будет жить)
Является ли архитектура сервиса, состоящая из нескольких слоев абстракции, какой-то излишней или неправильной? Нет, это стандартный подход в архитектуре - вводить новые уровни абстракции. Даже шутка на этот счет есть) Spring, Hibernate и куча других библиотек - это тоже новые слои абстракции. Цель введения нового слоя - упростить использование какой-то библиотеки или адаптировать ее для новой предметной области.
Что плавно подводит нас к паттернам Адаптер, Прокси и иже с ними https://t.me/javaKotlinDevOps/124 Паттерны - штука полезная, и да, я снова об этом уже писал https://t.me/javaKotlinDevOps/52 )))
И последний (риторический) вопрос: являются ли принципы DRY - Don't Repeat Yourself - и Single Responsibility вредными? Наоборот, они делают код более устойчивым к изменениям и упрощают его изучение.
Но почему же тогда в мире ПО не редкость встретить вот такую фабрику фабрик фабрик: https://factoryfactoryfactory.net ?
Ответ: ООП, принципы и паттерны не заменяют здравый смысл и чувство меры)
#oop #patterns #craftmanship #dev_compromises
Является ли ООП - объекто-ориентированное программирование - чем-то плохим? Ответ - ну нет. Благодаря объектам мы можем воспроизвести в коде реальные бизнес-объекты, а это стирает барьеры между заказчиком, аналитиком и разработчиком. Конечно, есть альтернативные подходы, например, функциональный. Или если посмотреть в другую сторону - декларативный. Но ООП жил, жив и будет жить)
Является ли архитектура сервиса, состоящая из нескольких слоев абстракции, какой-то излишней или неправильной? Нет, это стандартный подход в архитектуре - вводить новые уровни абстракции. Даже шутка на этот счет есть) Spring, Hibernate и куча других библиотек - это тоже новые слои абстракции. Цель введения нового слоя - упростить использование какой-то библиотеки или адаптировать ее для новой предметной области.
Что плавно подводит нас к паттернам Адаптер, Прокси и иже с ними https://t.me/javaKotlinDevOps/124 Паттерны - штука полезная, и да, я снова об этом уже писал https://t.me/javaKotlinDevOps/52 )))
И последний (риторический) вопрос: являются ли принципы DRY - Don't Repeat Yourself - и Single Responsibility вредными? Наоборот, они делают код более устойчивым к изменениям и упрощают его изучение.
Но почему же тогда в мире ПО не редкость встретить вот такую фабрику фабрик фабрик: https://factoryfactoryfactory.net ?
Ответ: ООП, принципы и паттерны не заменяют здравый смысл и чувство меры)
#oop #patterns #craftmanship #dev_compromises
Enterprise Craftsmanship
Domain model purity vs. domain model completeness (DDD Trilemma)
I’ve been meaning to write this article for a long time and, finally, here it is: the topic of domain model purity versus domain model completeness.