Многие используют подход API First в проектировании систем, но зачастую даже не задумываются, что порождают таким образом сервисы с анемичными доменными моделями, проектируя по сути REST-обертки над базой данных с отсутствием какой-либо бизнес-логики.
API First и Богатая доменная модель не являются взаимоисключающими понятиями, если правильно выстроить процесс и архитектуру.
Главная проблема: API First фокусируется на контрактах внешнего мира, что часто приводит к созданию DTO/Model классов, которые являются чистыми структурами данных без поведения (анемичная модель).
API First — это про контракты, а не про реализацию. Не позволяйте инструментам кодогенерации диктовать структуру вашей доменной модели!
Правильный workflow:
1. API Design First: Создайте/обновите OpenAPI спецификацию.
2. Генерация DTO: Сгенерируйте или создайте классы для API‑контрактов.
3. Projection First: Спроектируйте доменную модель независимо от DTO, фокусируясь на поведении и инвариантах.
4. Создание Mapping Layer: Напишите преобразователи между DTO и Domain Objects.
5. Реализация Application Service: Тонкий слой, который координирует работу домена.
Стек технологий, который помогает:
— MapStruct: Для автоматизации маппинга между DTO и Domain
— JUnit 5: Для тестирования доменной логики
— OpenAPI Generator: Для автоматической генерации DTO из спецификации
— ArchUnit: Для проверки архитектурных правил (например, запрет на анемичные модели)
// ArchUnit тест, проверяющий, что в доменных классах есть поведение @ArchTest static final ArchRule domain_models_should_have_business_methods = classes() .that().resideInPackage("..domain..") .and().areAnnotatedWith(Entity.class) .should().containAnyMethodsThat( DescribedPredicate.describe( "have business methods", method -> !method.getName().startsWith("get") && !method.getName().startsWith("set") ) );
