A multi-module Maven project demonstrating the integration of the Java Platform Module System (JPMS) with Domain-Driven Design (DDD) and Clean / Hexagonal Architecture principles.
The objective of this project is to model a decoupled payment system with asynchronous in-memory event publishing. By leveraging JPMS modules, compile-time and run-time encapsulation are strictly enforced, while DDD patterns ensure the business logic remains pure and decoupled from infrastructure components.
The project is split into four distinct modules under a parent POM:
graph TD
subgraph Application Layer
App[system.application]
end
subgraph Infrastructure Layer
Broker[system.messagebroker]
Utils[system.utils]
end
subgraph Domain Layer
Payment[system.payment]
end
App --> Payment
App --> Broker
App --> Utils
Broker --> Payment
Payment --> Utils
- Purpose: Represents the core business logic of the payment subdomain.
- Dependencies: Only depends on the utility module
system.utils. It knows nothing about the message broker or application bootstrap. - Key Patterns:
- Defines the
Transactionentity/record and its nestedCompletedEventrecord. - Declares the Port (Interface)
PaymentEventPublisherwhich abstraction-wise delegates the publication of domain events. - Encapsulates implementation details (
MockPayment) within a non-exported package, exposing only the service contract (Payment) and the creator factory (PaymentFactory).
- Defines the
- Purpose: A reusable, domain-agnostic in-memory event bus implementing Java's Flow API (
SubmissionPublisher). - Dependencies: None. It has no dependencies on
system.payment, making it 100% reusable across other domains (e.g., inventory, ordering). - Key Patterns:
- Exposes a generic
EventPortinterface for subscribing and publishing arbitrary events.
- Exposes a generic
- Purpose: Provides helper tools such as a reflection-based custom JSON serializer (
Json). - Key JPMS Concept: Demonstrates deep reflection using the
opensdirective in the module-info descriptor of target classes (e.g., opening payment models tosystem.utilsto allow reading private fields at runtime).
- Purpose: The entry point of the application. It acts as the bootstrapper that configures and wires all components.
- Dependencies: Depends on all modules (
system.payment,system.messagebroker,system.utils). - Key Patterns:
- Implements the Adapter Pattern via
PaymentEventPublisherAdapterto bridge the domain-specificPaymentEventPublisherinterface with the genericEventPortbus. - Bootstraps the application, configures event listeners, and triggers mock payment executions.
- Implements the Adapter Pattern via
To keep the system.messagebroker generic and the system.payment domain pure, we decoupled them through an adapter in the application layer:
system.paymentdefines a port:PaymentEventPublisher.system.messagebrokerdefines a generic bus:EventPort.system.applicationbridges them withPaymentEventPublisherAdapter, avoiding circular or domain-to-infrastructure dependencies.
- The payment module uses
exportsto expose contracts (Payment,PaymentFactory,Transaction). - It uses
opens com.watashi.system.payment.model to system.utilsto permit the serializer to perform deep reflection on model classes without exposing internal fields to compiler imports.
- Java SDK 21 or higher
- Apache Maven 3.9+
Run the clean install command to compile, build modular JARs, and install them into your local maven cache:
mvn clean installExecute the bootstrap main class via the Maven exec plugin:
mvn exec:java -pl system.applicationCreated as a study workspace for exploring modular Java architectures.