Boost Java Backend Development with DDD, CQRS, and Automated Maven Archetype Generation

This article explains how to lower the learning curve of DDD and CQRS by using structured, standardized, and templated approaches, introduces a Maven archetype for rapid project scaffolding, and demonstrates an IntelliJ IDEA plugin that auto‑generates boilerplate code for aggregate roots, commands, queries, and related components.

macrozheng
macrozheng
macrozheng
Boost Java Backend Development with DDD, CQRS, and Automated Maven Archetype Generation

1. Background

Domain‑Driven Design (DDD) is known for its high entry barrier, introducing many abstract concepts such as entities, value objects, domain services, aggregates, factories, repositories, and application services. Newcomers often feel overwhelmed, especially when additional patterns like CQRS, hexagonal architecture, adapters, and anti‑corruption layers are added.

The author reflects on the difficulty of mastering these concepts individually and the challenge of aligning an entire team.

2. Purpose

The goals are to:

Reduce the memory cost of concepts so junior developers are not intimidated by DDD.

Unify standards so business processes and component designs remain consistent across the team.

Improve development efficiency by letting machines handle repetitive logic.

3. Feature Overview

3.1 Maven Archetype

The Maven archetype creates a highly unified project structure, lowering the cost of starting a new project.

Run the following command to generate a project that follows company conventions:

mvn archetype:generate -DarchetypeGroupId=com.geekhalo.lego -DarchetypeArtifactId=services-archetype -DarchetypeVersion=0.1.39-plugin_demo-SNAPSHOT -DgroupId=com.geekhalo -DartifactId=user -Dversion=0.1.39-plugin_demo-SNAPSHOT

The command creates a new project based on the services-archetype template with the specified groupId, artifactId, and version.

After execution, a user module appears with the following sub‑modules: user-domain: core domain models and logic (hexagonal core layer). user-infrastructure: technical implementations such as database access. user-app: application logic that depends on domain and infrastructure. user-api: external service contracts (Feign RPC and RocketMQ). user-feign-service and user-feign-client: SDK for other microservices. user-bootstrap: Spring Boot starter class.

The root contains pom.xml (Maven configuration) and README.md (project documentation).

3.2 Custom IDEA Plugin

After scaffolding, the IDEA plugin integrates the standards, dramatically reducing development cost.

3.2.1 Create Aggregate Root and View Model

Using the plugin, create an aggregate root “BasicUser” in the basic package of the domain module.

The plugin generates the necessary files across modules (domain, infrastructure, app, etc.).

3.2.2 Create Aggregate Root Method

Right‑click the aggregate root and select “Create Aggregate Root Method”. Define the method name as create. The plugin generates: CreateBasicUserCommand: command object with required parameters. CreateBasicUserContext: context object holding environment data. BasicUserCreatedEvent: event indicating successful creation.

It also adds static create and instance init methods to BasicUser, and a create method in BasicUserCommandApplication that coordinates the flow.

No manual service code is needed; the framework generates proxy classes to complete the process.

3.2.3 Create Query Method

In BasicUserQueryApplication, select “Create Query Method” and choose pagination. The plugin generates the following interface:

@QueryServiceDefinition(repositoryClass = BasicUserQueryRepository.class, domainClass = BasicUserView.class)
@Validated
public interface BasicUserQueryApplication {
    // Pagination query method
    Page<BasicUserView> pageOf(@Valid PageByStatus query);
}

The PageByStatus query object is defined as:

@NoArgsConstructor
@Data
public class PageByStatus {
    @FieldEqualTo("status")
    private UserStatus status;
    private Pageable pageable;
    private Sort sort;
}

This provides filtering by status, pagination, and sorting.

3.2.4 Additional Support

3.2.4.1 Common Enum

Creating an enum generates the enum class and a JPA converter. Example:

public enum UserStatus implements CommonEnum {
    // code and description fields
    private final int code;
    private final String descr;
    UserStatus(int code, String descr) { this.code = code; this.descr = descr; }
    @Override public int getCode() { return this.code; }
    @Override public String getDescription() { return this.descr; }
}
@Converter(autoApply = true)
public class UserStatusConverter extends CommonEnumAttributeConverter<UserStatus> {
    public UserStatusConverter() { super(UserStatus.values()); }
}

3.2.4.2 Context Factory

@Component
@Slf4j
public class CreateBasicUserContextFactory extends AbstractSmartContextFactory<CreateBasicUserCommand, CreateBasicUserContext> {
    public CreateBasicUserContextFactory() { super(CreateBasicUserCommand.class, CreateBasicUserContext.class); }
    @Override
    public CreateBasicUserContext create(CreateBasicUserCommand cmd) {
        return CreateBasicUserContext.apply(cmd);
    }
}

3.2.4.3 Aggregate Root Factory

@Component
@Slf4j
public class BasicUserFactory extends AbstractSmartAggFactory<CreateBasicUserContext, BasicUser> {
    public BasicUserFactory() { super(CreateBasicUserContext.class, BasicUser.class); }
    @Override
    public BasicUser create(CreateBasicUserContext context) {
        return BasicUser.create(context);
    }
}

3.2.4.4 Business Validator

@Component
@Slf4j
public class CreateBasicUserPhoneValidator extends AbstractBusinessValidator<CreateBasicUserContext> {
    public CreateBasicUserPhoneValidator() { super(CreateBasicUserContext.class); }
    @Override
    public void validate(CreateBasicUserContext context, ValidateErrorHandler validateErrorHandler) {
        log.info("validate(context) {}", context);
    }
}

3.2.4.5 Result Converter

@Component
@Slf4j
public class BasicUserKeyConverter extends AbstractSmartResultConverter<BasicUser, CreateBasicUserContext, BasicUserKey> {
    public BasicUserKeyConverter() { super(BasicUser.class, CreateBasicUserContext.class, BasicUserKey.class); }
    @Override
    public BasicUserKey convert(BasicUser agg, CreateBasicUserContext context) {
        // conversion logic
        return null;
    }
}

These generated components together provide a complete DDD‑oriented, CQRS‑compatible skeleton for the user service, allowing developers to focus on business logic while the framework handles boilerplate.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javabackend-developmentmavenDDDSpringBootCQRS
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.