A lightweight, flexible library for building modular, event-driven apps using CQRS. Use built-in command, query, and event buses for simple setups, or extend them for complex architectures. Portable, minimal boilerplate, and adaptable to any ecosystem.
- Nexus
- π Table of Contents
- ποΈ Architecture Diagram
- π Features
- π¦ Installation
- π Quick Start
- π Documentation
- ποΈ Architecture
- π§ͺ Testing
- β‘ Performance
- π§ Advanced Usage
- π Error Handling
- π€ Contributing
- π License
- π Acknowledgments
- π Support
- Built with β€οΈ by RegreDanger
- Constructor-based injection with
@Injectannotation - Hierarchical registration with
@Injectable(level = N)for dependency ordering - Static method registration via
@Managedfor singleton/factory beans - Automatic class scanning and registration
- Command/Query separation with type-safe handlers
- Generic handler interface supporting any input/output types
- Centralized bus for decoupled command/query execution
- Automatic handler discovery and registration
- Domain Event publishing and handling
- Multiple handlers per event type support
- Asynchronous event processing ready
- Type-safe event handling with generics
- Zero reflection overhead in runtime execution
- Immutable registries for thread safety
- Lazy initialization where possible
- Memory optimized with pre-sized collections
- Comprehensive error handling with descriptive messages
Run the build.bat or build.sh
Then add this on your pom.xml
<dependency>
<groupId>com.nexus</groupId>
<artifactId>nexus</artifactId>
<version>1.0.0</version>
</dependency>- Java 21 or higher
- Maven 3.8+
// Domain Event
public class UserRegistered implements DomainEvent {
private final String userId;
private final String email;
public UserRegistered(String userId, String email) {
this.userId = userId;
this.email = email;
}
// getters...
}
// Command
public class RegisterUserCommand implements Command<RegisterUserRequest, String> {
private final UserService userService;
@Inject
public RegisterUserCommand(UserService userService) {
this.userService = userService;
}
@Override
public String handle(RegisterUserRequest request) {
// Business logic
return userService.registerUser(request);
}
}
// Event Handler
public class UserRegisteredHandler implements EventHandler<UserRegistered> {
private final EmailService emailService;
@Inject
public UserRegisteredHandler(EmailService emailService) {
this.emailService = emailService;
}
@Override
public void on(UserRegistered event) {
emailService.sendWelcomeEmail(event.getEmail());
}
}// Injectable Services
@Injectable
public class UserService {
private final UserRepository repository;
@Inject
public UserService(UserRepository repository) {
this.repository = repository;
}
}
// Configuration Class for singletons
@WiringConfig
public class Bar {
@Managed
public static foo() {
return Foo.getinstance();
}
}public class Application {
public static void main(String[] args) {
// Build Nexus context
NexusContext context = new NexusContext.NexusContextBuilder()
.packagesToScan("com.myapp.domain", "com.myapp.handlers")
.build();
// Get buses
CqrsBus cqrsBus = context.getCqrsBus();
EventBus eventBus = context.getEventBus();
// Execute commands
RegisterUserRequest request = new RegisterUserRequest("john@doe.com");
String userId = cqrsBus.send(RegisterUserCommand.class, request);
// Publish events
eventBus.publish(UserRegistered.class, new UserRegistered(userId, "john@doe.com"));
}
}| Annotation | Target | Purpose |
|---|---|---|
@Inject |
Constructor | Marks constructor for dependency injection |
@Injectable |
Class | Auto-register class with optional level ordering |
@Managed |
Method | Register static method return value as dependency |
@WiringConfig |
Class | Mark class as containing @Managed methods |
NexusContext context = new NexusContext.NexusContextBuilder()
.packagesToScan("com.myapp") // Scan packages
.withCqrsBus(customCqrsBus) // Optional: custom CQRS bus
.withEventBus(customEventBus) // Optional: custom Event bus
.onlyCqrs() // Optional: disable event bus
.onlyEventBus() // Optional: disable CQRS bus
.build();Classes with WiringConfig annotation will be registered first, use only for singletons
Control dependency registration order with levels:
@Injectable(level = 0) // Registered first, if you don't specify level, the default it's 0
public class DatabaseConnection { /* ... */ }
@Injectable(level = 1) // Registered after level 0
public class UserRepository {
@Inject
public UserRepository(DatabaseConnection connection) { /* ... */ }
}
@Injectable(level = 2) // Registered after level 1
public class UserService {
@Inject
public UserService(UserRepository repository) { /* ... */ }
}All components use the Registry Pattern for centralized management:
- PackagesRegistry: Scans and discovers classes
- DependencyRegistry: Manages dependency instances
- ManagedRegistry: Handles
@Managedmethod registration - InjectableRegistry: Processes
@Injectableclasses - CqrsHandlersRegistry: Manages Command/Query handlers
- EventHandlersRegistry: Manages Event handlers
- NexusCqrsBus: Thread-safe CQRS command/query execution
- NexusEventBus: Thread-safe event publishing with multiple handlers
The framework includes comprehensive test coverage:
mvn test- Unit Tests: Individual component testing
- Integration Tests: Full framework flow testing
@Test
void shouldInjectDependenciesAndExecuteCommand() {
NexusContext context = new NexusContext.NexusContextBuilder()
.packagesToScan("com.example.test")
.build();
CqrsBus bus = context.getCqrsBus();
String result = bus.send(TestCommand.class, "input");
assertEquals("expected", result);
}- Context initialization: ~71ms for 100 classes
- Dependency resolution per injection: ~0.000032β―ms (3.2Γ10β»β΅β―ms)
- Command/Query execution: ~0.1799β―ms (for empty command/query invocation)
- Event publishing: ~0.4266β―ms
- No reflection caching: Resolved at startup only
public class AsyncEventBus implements EventBus {
private final EventHandlersRegistry registry;
private final ExecutorService executor;
@Override
public <T extends DomainEvent> void publish(Class<T> eventType, T event) {
List<EventHandler<T>> handlers = registry.getHandlers(eventType);
handlers.forEach(handler ->
executor.submit(() -> handler.on(event))
);
}
}
// Use custom bus
NexusContext context = new NexusContext.NexusContextBuilder()
.packagesToScan("com.myapp")
.withEventBus(new AsyncEventBus(registry, executor))
.build();// Multiple handlers for the same event
public class EmailNotificationHandler implements EventHandler<UserRegistered> {
@Override
public void on(UserRegistered event) {
// Send email
}
}
public class AuditLogHandler implements EventHandler<UserRegistered> {
@Override
public void on(UserRegistered event) {
// Log to audit system
}
}
// Both handlers will be called automatically
eventBus.publish(UserRegistered.class, event);The framework provides detailed error messages:
// Example error messages
"CQRS handler not found: no handler of type UserCommand is registered.
Ensure the class implements Command<T, R> or Query<T, R>, is annotated with @Inject,
and is located in a package being scanned by NexusContext."
"Dependency not found: no instance of UserService is registered in the dependency registry.
Make sure the class is annotated with @Injectable or registered via @Managed,
and that it's in a package being scanned by NexusContext."- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing-feature) - Open a Pull Request
git clone https://github.com/RegreDanger/nexus.git
cd nexus
mvn clean install
mvn testThis project is licensed under the MIT License - see the LICENSE file for details.
- Inspired by Spring Framework's IoC container
- CQRS pattern implementation influenced by Axon Framework
- Event Bus design inspired by Google Guava EventBus
- Issues: GitHub Issues
