Одна статья, которая поможет вам легко начать работу с предметно-ориентированным проектированием (DDD) | Основы для архитекторов (1)

Предисловие

В повседневной работе большинство проектов, которые мы берем на себя или обслуживаем, используют трехуровневую архитектуру, а именно контроллер, сервис и дао.В процессе использования вы столкнетесь со многими проблемами:

  • Данные-ориентированное моделирование, процессно-ориентированное программирование, никакой настоящей «объектно-ориентированности».
  • Сосредотачиваясь только на результатах, а не на процессе, уровень обслуживания часто имеет сотни или тысячи строк, полных кода процесса и связующего кода, которые либо раздуты, имеют запущенные учетные записи, повторяются, либо логически разбросаны, что чрезвычайно затрудняет поддержание их в рабочем состоянии. будущее.
  • Связь кода серьезная, слои вызывают друг друга и выполняют обратные вызовы, что влияет на все тело.
  • Код не может отражать бизнес.Когда никто не любит писать комментарии, со временем никто не поймет бизнес-логику кода и никто не посмеет ее изменить.

Так есть ли хорошее решение? DDD, о котором мы поговорим сегодня, — хороший выбор.

ДДД

DDD, или доменно-ориентированный дизайн , прекрасно решает вышеперечисленные проблемы:

  • Доменно-ориентированное моделирование, объектно-ориентированное программирование, код напрямую отображает реальные концепции, ближе к бизнесу, ближе к клиентам
  • Логика предметной области очень связна и соответствует принципам разработки Java.
  • Изменения в технических деталях, таких как база данных, кэш, таймер и т. д., оказывают относительно небольшое влияние на бизнес-логику и очень подходят для подключаемой архитектуры.
  • Код более читабелен и удобен в сопровождении, имеет лучшую поддержку для последующего расширения, трансплантации и т. д., а многоуровневость является более научной.

Концепцию DDD легко найти в Интернете, поэтому я не буду здесь вдаваться в подробности.

Однако, хотя в Интернете есть много статей по DDD, большинство из них представляют собой теоретические знания, а введение представляет собой не что иное, как некоторые термины: стратегический дизайн, тактический дизайн, основная область, область поддержки, объект значения, сущность, агрегация. ...но они не очень полезны для нашей фактической реализации.Спасибо за вашу помощь.Вот мой план реализации применения DDD в SpringBoot.

План реализации

1. Многоуровневое кодирование

изображение

Многоуровневое кодирование

  • Уровень пользовательского интерфейса : пакет API на картинке (то есть уровень контроллера, я думаю, что суффикс контроллера слишком длинный...)
  • Уровень приложения : Здесь используется командный режим, а чтение и запись разделены на два пакета (команда, запрос).Если командный режим не используется, его можно объединить в один сервисный пакет.
  • Уровень домена : пакет домена, использующий JPA (имеет хорошую поддержку DDD).
  • Уровень инфраструктуры : инфраструктурный пакет, здесь размещаются все остальные общедоступные компоненты. Если используется инверсия зависимостей DIP, здесь также размещается класс реализации.
  • модель модели : пакет модели, используемый для хранения объектов, передаваемых между разными слоями.Я пробовал размещать эти объекты во многих местах, но в конце концов обнаружил, что лучше поместить их в один пакет (чтобы облегчить совместное использование объектов при вызове между службами)

2. Иерархические отношения и перенос модели

изображение

Многоуровневые и вызывающие отношения

3. Подробное описание наслоения

  • пакет API (контроллер)
@Tag(name = "用户", description = "用户")
@RestController
@RequestMapping(value = "/api/sys-user")
public class SysUserApi extends BaseApi {
    @ApiResult
    @Operation(summary = "根据ID查询用户")
    @GetMapping("/{id}")
    public SysUserVo get(@PathVariable Long id) {
        return queryExecutor.execute(new SysUserByIdQry(id));
    }
    @Pagination(total = true)
    @ApiResult
    @Operation(summary = "分页查询用户")
    @GetMapping
    public List<SysUserVo> getList(SysUserQo sysUserQo) {
        return queryExecutor.execute(new SysUserListQry(sysUserQo));
    }
    @ApiResult
    @Operation(summary = "新增用户")
    @PostMapping
    public void save(@Valid @RequestBody SysUserDto sysUserDto) {
        commandExecutor.execute(new SysUserCommonCmd(sysUserDto));
    }
}

Два класса выполнения команд, queryExecutor и CommandExecutor, инкапсулированы в BaseApi. Различные команды могут выполняться при вызове уровня приложения без @Autowired, вводящего различные службы.

После того как @ApiResult добавит эту пользовательскую аннотацию, возвращаемые результаты будут равномерно инкапсулированы.

@Pagination После добавления этой пользовательской аннотации параметры разбиения на страницы будут автоматически сохраняться в переменной потока. Параметры разбиения на страницы также будут автоматически получены во время последующих запросов. Информация о разбиении на страницы также будет добавлена, когда возвращаемые результаты будут унифицированы и упакованы.

Qo — объект параметра запроса, Dto — объект параметра команды, такой как добавление, удаление и изменение. Возвращаемый объект — Vo. Здесь следует отметить, что Entity не должен быть доступен этому уровню и его необходимо преобразовать в Vo, а затем вернулся.

На этом уровне каждый метод представляет собой почти строку операторов, выполняющих команды. Как правило, никакая бизнес-логика не выполняется (конечно, есть особые случаи).

  • пакет команд
@AllArgsConstructor
public class SysDeptAddCmd implements Command<Void> {
    private SysDeptDto sysDeptDto;
    @Override
    public Void execute(Executor executor) {
        // 获取命令的接收者:领域服务
        SysDeptManager receiver = executor.getReceiver(SysDeptManager.class);
        // 对象模型转换,由DTO转为Entity,使用了MapStruct
        SysDept sysDept = SysDeptMapper.INSTANCE.toSysDept(sysDeptDto);
        // 使用JPA保存
        receiver.save(sysDept);
        return null;
    }
}

Добавлять, удалять и изменять команды, очень тонкий слой, как организатор работы, практически без бизнес-логики, вызовы доменных сервисов и методов объекта перегрузки

Командный режим реализует собственный командный интерфейс, а дженерики являются возвращаемыми значениями.

Получение параметров через свойства и конструкторы (с использованием аннотаций Lombok)

В команде есть только один метод выполнения.Недостаток заключается в том, что будет сгенерировано большое количество классов команд.Каждый класс эквивалентен методу в предыдущем классе обслуживания, но это соответствует принципу единой ответственности.

Получите службу домена (менеджер) с помощью метода executor.getRecerver.

DTO никогда не проникнет в доменный слой. Сначала его необходимо преобразовать из DTO в Entity (используемый здесь MapStruct — это метод преобразования, который будет подробно обсуждаться позже).

  • пакет запроса
@AllArgsConstructor
public class SysDeptByIdQry extends CommonQry<SysDeptVo> {
    private Long id;
    @Override
    public SysDeptVo execute(Executor executor) {
        if (id == null) {
            throw new BusinessException("部门ID不能为空");
        }
        QSysDept sysDept = QSysDept.sysDept;
        return queryFactory.select(this.fields())
                .from(sysDept)
                .where(sysDept.deleted.eq(false), sysDept.id.eq(id))
                .fetchOne();
    }
    /**
     * 部门VO映射
     *
     * @return QBean<SysDeptVo>
     */
    public static QBean<SysDeptVo> fields() {
        QSysDept sysDept = QSysDept.sysDept;
        return Projections.fields(
            SysDeptVo.class,
            sysDept.deptName,
            sysDept.orderNum,
            sysDept.id
        );
    }
}

Командный режим наследует пользовательский базовый класс CommonQry (этот класс также реализует пользовательский командный интерфейс, который ссылается на класс queryFactory QueryDSL и инкапсулирует метод подкачки), а общий тип — это возвращаемое значение.

Команды запроса в пакете запросов примерно такие же, как и команды в пакете команд, с той лишь разницей, что команда запроса теоретически напрямую запрашивает базу данных, не вызывая уровень домена.

Поскольку JPA непросто использовать для сложных запросов, настоятельно рекомендуется использовать QueryDSL (подробно об этом будет сказано позже).На рисунке показан простой пример использования.

  • доменный пакет

Классов много, а части кода не перечислены по одному:

Таблица базы данных автоматически генерируется классом Entity, и поддерживается только класс Entity (защита базы данных).

При проектировании Entity гибко используйте @OneToMany, @OneToOne и другие аннотации в соответствии с реальным бизнесом (концепция совокупного корня).

Корень агрегата не должен быть слишком большим. В 80% случаев корень агрегата содержит только один объект (не переусердствуйте с созданием большого корня агрегата).

Не используйте анемичную модель, а будьте объектно-ориентированной. Методы, принадлежащие объектам, должны быть помещены в объекты. Однако не рекомендуется вводить класс репозитория в объекты. Методы, которые должны работать с базой данных, должны быть написаны на языке менеджер службы домена.

Бизнес-логика должна быть написана в доменной службе (менеджере), насколько это возможно, а различные методы должны непрерывно извлекаться и абстрагироваться для вызовов уровня приложения.

При соответствующем использовании событий домена JPA может использовать аннотацию @DomainEvents в Entity для отправки событий домена.

Опыт

  • С помощью DDD вы сможете лучше понять бизнес, а написанный вами код сможет лучше передать бизнес-требования клиента.
  • Очень приятно иметь возможность писать код с низкой степенью связи, соответствующий принципам единой ответственности, открытия и закрытия, инкапсуляции, наследования и полиморфизма.
  • По сравнению с традиционной архитектурой объем кода на ранней стадии больше, и разработчики вкладывают больше средств на ранней стадии:
  • Разумное разделение полей и разумный дизайн сущностей
  • Большое количество DTO, VO и других объектов данных.
  • Большое количество методов преобразования объектов данных
  • Большое количество командных классов
  • ...

Однако, если это не особенно простая функция, для системы средней сложности эти предварительные усилия все равно того стоят.

краткое содержание

Вышеупомянутое кратко знакомит с моим пониманием и практикой DDD и показывает, как применять DDD в SpringBoot с помощью реального кода. Надеюсь, это может дать вам представление.

Эта статья опубликована OpenWrite, блогом, в котором публикуется множество статей !

OpenAI бесплатно открывает ChatGPT для всех пользователей. Голосовые программисты подделали балансы ETC и присвоили более 2,6 млн юаней в год. Официально выпущена Spring Boot 3.2.0. Сотрудники Google раскритиковали большого босса после ухода из компании. Он принимал активное участие в проект Flutter и сформулировал стандарты, связанные с HTML. Microsoft Copilot Web AI будет официально запущен 1 декабря и будет поддерживать китайскую веб-инфраструктуру Terminal Chat Rust от Microsoft с открытым исходным кодом. Rocket-версии v0.5: поддерживает асинхронный режим, SSE, WebSockets и т. д . Отец Redis реализует фреймворк Telegram Bot с использованием чистого кода языка C. Если вы являетесь сопровождающим проекта с открытым исходным кодом, столкнитесь с вопросом: «Как далеко вы сможете вынести такой ответ?» PHP 8.3 общедоступная версия
{{o.name}}
{{м.имя}}

Supongo que te gusta

Origin my.oschina.net/u/5587102/blog/10141477
Recomendado
Clasificación