java-ddd-example

repository·main·Indexed 19 days ago

https://github.com/codelytv/java-ddd-example

A bootstrap repository for Java projects using Gradle and JUnit, designed with Domain-Driven Design (DDD) principles. It includes infrastructure for MySQL, RabbitMQ, and Elasticsearch via Docker Compose, and provides a shared domain bus implementation featuring CommandBus, CommandHandler, and InMemoryCommandBus for decoupling command dispatching from execution.

Tokens
2.8K
Snippets
12
Records
16
Agent score
66%

What's inside java-ddd-example

  1. Update Gradle version in the project

    main

    To update the Gradle wrapper to a specific version, use the ./gradlew command with the wrapper task. Replace WANTED_VERSION with your desired version number.

    ./gradlew wrapper --gradle-version=WANTED_VERSION --distribution-type=bin
  2. Get started with the Java DDD example project

    main

    This repository provides a bootstrap for Java projects using JUnit and Gradle, following Domain-Driven Design (DDD) principles. To set up your local environment and verify the project, follow these steps:

    1. Install Java 11: Use Homebrew to install Amazon Corretto.
    2. Configure JVM: Set your JAVA_HOME environment variable.
    3. Clone the repository: Download the source code.
    4. Start Infrastructure: Use make up to bring up the Docker environment.
    5. Verify Installation: Run Gradle tasks via make to ensure the build and tests are working correctly.

    Verification Commands

    • Build the project JAR: make build
    • Run tests and plugin verification: make test
    # 1. Install Java 11
    brew cask install corretto
    
    # 2. Set JAVA_HOME
    export JAVA_HOME='/Library/Java/JavaVirtualMachines/amazon-corretto-11.jdk/Contents/Home'
    
    # 3. Clone
    git clone https://github.com/CodelyTV/java-ddd-example
    
    # 4. Start Docker environment
    make up
    
    # 5. Verify
    make build
    make test
  3. Run Java application servers via Docker Compose

    main

    The repository defines several Java application services that can be started using Docker Compose. Each service builds from the local Dockerfile and uses ./gradlew bootRun to start the server.

    Available application services:

    • backoffice_backend_server_java: Runs on port 8040
    • backoffice_frontend_server_java: Runs on port 8041
    • mooc_backend_server_java: Runs on port 8030
    • test_server_java: A specialized container for testing (no default port mapping).
    docker-compose up
  4. Implement a custom CLI command by extending ConsoleCommand

    main

    To create a new command-line interface (CLI) command, extend the ConsoleCommand abstract class and implement the execute(String[] args) method. This method serves as the entry point for your command logic, receiving any command-line arguments as a String array.

    Use the provided protected logging methods to output formatted messages to the console:

    • log(String text): Prints text in green.
    • info(String text): Prints text in cyan.
    • error(String text): Prints text in red.
    import tv.codely.shared.infrastructure.cli.ConsoleCommand;
    
    public class MyCustomCommand extends ConsoleCommand {
    
        @Override
        public void execute(String[] args) {
            if (args.length == 0) {
                error("No arguments provided!");
                return;
            }
            
            info("Starting command execution...");
            log("Command executed successfully with argument: " + args[0]);
        }
    }
  5. Dispatch commands using InMemoryCommandBus

    main

    The InMemoryCommandBus is a Spring-managed implementation of the CommandBus interface designed for local execution. It facilitates the decoupling of command dispatching from command handling by using an internal registry (CommandHandlersInformation) to locate the appropriate CommandHandler within the Spring ApplicationContext.

    To use it, call the dispatch method with a Command instance. The bus will automatically resolve the correct handler and execute its handle method. If the handler execution fails, the bus wraps the underlying error in a CommandHandlerExecutionError.

    // Assuming command and commandBus are already instantiated
    try {
        commandBus.dispatch(command);
    } catch (CommandHandlerExecutionError error) {
        // Handle execution failure
    }
  6. Dispatch commands using the CommandBus interface

    main

    The CommandBus is an interface used to dispatch Command objects to their respective handlers within the domain layer. Use the dispatch method to trigger a command execution. If the command handler fails, it will throw a CommandHandlerExecutionError.

    // Example usage of CommandBus
    commandBus.dispatch(new MyCommand(parameters));
  7. Accessing shared infrastructure ports

    main

    When running the environment via Docker Compose, use the following host port mappings to connect to the infrastructure services from your local machine:

    • MySQL: 3306
    • RabbitMQ Management UI: 8090
    • RabbitMQ Protocol: 5630
    • Elasticsearch: 9200 (HTTP) and 9300 (Transport)
  8. Handle CommandNotRegisteredError

    main
    The CommandNotRegisteredError is thrown by the command bus when an attempt is made to execute a Command that does not have a corresponding registered handler in the system. This error indicates a configuration or registration issue within the command bus infrastructure. When catching this error, the exception provides the class of the command that failed to find a handler via its constructor.
  9. Infrastructure services in docker-compose.yml

    main

    The project uses Docker Compose to orchestrate several shared infrastructure services required by the Java applications. These services include MySQL for persistence, RabbitMQ for messaging, and Elasticsearch for search capabilities.

    services:
      shared_mysql:
        image: mysql:8
        ports:
          - "3306:3306"
    
      shared_rabbitmq:
        image: 'rabbitmq:3.7-management'
        ports:
          - "5630:5672"
          - "8090:15672"
        environment:
          - RABBITMQ_DEFAULT_USER=codely
          - RABBITMQ_DEFAULT_PASS=c0d3ly
    
      backoffice_elasticsearch:
        image: 'elasticsearch:6.8.4'
        ports:
          - "9300:9300"
          - "9200:9200"
        environment:
          - discovery.type=single-node
  10. Application service command arguments

    main

    The Java services are configured to run specific server profiles using Gradle arguments. If you are modifying the docker-compose.yml or running commands manually, note the following patterns:

    • Backoffice Backend: ./gradlew bootRun --args="backoffice_backend server"
    • Backoffice Frontend: ./gradlew bootRun --args="backoffice_frontend server"
    • MOOC Backend: ./gradlew bootRun --args="mooc_backend server"
  11. Consume MySQL domain events via CLI

    main

    The ConsumeMySqlDomainEventsCommand is a console command used to trigger the consumption of domain events stored in MySQL. When executed, it invokes the MySqlDomainEventsConsumer to process the event stream. This is typically used in a CLI environment to start a worker process that listens for and handles domain events asynchronously.

    // This command is intended to be run via a CLI entrypoint.
    // It executes the following logic:
    consumer.consume();
  12. Consume RabbitMQ domain events via CLI

    main

    The ConsumeRabbitMqDomainEventsCommand is a CLI entrypoint used to start a process that listens for and consumes domain events from RabbitMQ. When executed, it invokes the RabbitMqDomainEventsConsumer to begin the consumption loop. This is typically used in a standalone worker process or a containerized environment to handle asynchronous domain events.

    // This command is intended to be run via the application's CLI entrypoint
    // It triggers the RabbitMqDomainEventsConsumer.consume() method