clean-architecture-kernel
v1.1.1
Published
A lightweight, zero-dependency npm package for implementing Clean Architecture, DDD, and CQRS patterns with MediatR-inspired features
Maintainers
Readme
Clean Architecture Kernel
A lightweight, zero-dependency npm package for implementing Clean Architecture, Domain-Driven Design, and CQRS patterns in Node.js applications.
Overview
Overview
Clean Architecture Kernel provides the essential building blocks for implementing Clean Architecture, Domain-Driven Design (DDD), and CQRS patterns in your application.
Why Clean Architecture Kernel?
This package focuses on core domain patterns only, ensuring the kernel remains:
- Small - Minimal footprint, zero external dependencies
- Fast - No abstractions, direct pattern implementations
- Framework-agnostic - Works with any Node.js framework or runtime
- Type-safe - Built with TypeScript for complete type safety
Universal Primitives
Result<T>- Functional success/failure wrapperEntity- Domain entities with identity and eventsAggregateRoot- Aggregate consistency boundariesValueObject- Immutable property-defined objectsDomainEvent- Domain occurrence representationsMediator- In-memory command/query dispatcher
Perfect For
- Backend services & REST APIs
- Microservices architectures
- Serverless functions
- Monolithic applications
- Event-driven systems
Table of Contents
- Installation
- Core Concepts
- Usage Examples
- Project Structure
- Advanced Features
- Design Principles
- Roadmap
- Contributing
- License
Installation
Choose your Node.js package manager:
NPM
npm install clean-architecture-kernelYarn
yarn add clean-architecture-kernelPNPM
pnpm add clean-architecture-kernelCore Concepts
Core Concepts
Result
A functional wrapper that encapsulates success or failure states, eliminating the need for try-catch blocks.
const result = Result.ok(user);
if (result.isFailure) {
console.log(result.error);
}Entity
Base class for domain entities with identity and domain event support.
AggregateRoot
Specialized entity that acts as a consistency boundary for related entities.
ValueObject
Immutable object defined by its properties rather than identity, ensuring data integrity.
DomainEvent
Represents something that happened inside the domain, enabling event-driven architectures.
Mediator
Simple in-memory mediator pattern for dispatching commands and queries.
Project Structure
Project Structure
src/
├── core/
│ ├── Result.ts # Success/failure wrapper
│ ├── Entity.ts # Domain entity base class
│ ├── AggregateRoot.ts # Aggregate consistency boundary
│ ├── ValueObject.ts # Immutable value object
│ ├── DomainEvent.ts # Domain event base class
│ └── Mediator.ts # Command/query dispatcher
└── index.ts # Public API exportsUsage Examples
1. Creating a Value Object
class Email extends ValueObject<{ value: string }> {
private constructor(props) {
super(props);
}
static create(value: string): Result<Email> {
if (!value.includes("@")) {
return Result.fail("Invalid email");
}
return Result.ok(new Email({ value }));
}
}2. Creating an Entity
class User extends AggregateRoot<string> {
constructor(id: string, public email: Email) {
super(id);
}
}3. Defining a Command
class CreateUserCommand {
constructor(public readonly email: string) {}
}4. Implementing a Command Handler
class CreateUserHandler {
async handle(command: CreateUserCommand) {
const emailResult = Email.create(command.email);
if (emailResult.isFailure) return emailResult;
const user = new User("123", emailResult.value);
return Result.ok(user);
}
}5. Using the Mediator
const mediator = new Mediator();
mediator.register("CreateUserCommand", new CreateUserHandler());
const result = await mediator.send(new CreateUserCommand("[email protected]"));Design Principles
Design Principles
✓ No Dependencies - Pure TypeScript/JavaScript, zero external npm packages
✓ Predictable Behavior - Consistent, deterministic pattern implementations
✓ Production-Ready - Optimized for Node.js backends and microservices
✓ Minimal API - Small surface area, easy to learn and understand
✓ Type-Safe - Full TypeScript support out of the box
✓ Inspired by - Domain-Driven Design, Clean Architecture, and CQRS
Advanced Features
Beyond the core patterns, clean-architecture-kernel includes powerful MediatR-inspired features:
Validators - Request Validation
Validate requests before they reach handlers:
import { Validator, ValidationResult, IRequest } from 'clean-architecture-kernel';
class CreateUserCommand implements IRequest<User> {
constructor(public readonly email: string) {}
}
class CreateUserValidator extends Validator<CreateUserCommand> {
validate(command: CreateUserCommand): ValidationResult {
const errors = [];
if (!command.email.includes('@')) {
errors.push({ property: 'email', message: 'Invalid email format' });
}
if (command.email.length > 100) {
errors.push({ property: 'email', message: 'Email too long' });
}
return errors.length > 0
? ValidationResult.failure(errors)
: ValidationResult.success();
}
}
// Register and use
const mediator = new Mediator();
mediator.registerValidator('CreateUserCommand', new CreateUserValidator());
mediator.registerHandler('CreateUserCommand', new CreateUserHandler());
try {
const user = await mediator.send(new CreateUserCommand('invalid'));
} catch (error) {
if (error instanceof ValidationError) {
console.log('Validation errors:', error.errors);
}
}Notifications - Publish-Subscribe Pattern
Trigger actions in response to domain events:
import { NotificationHandler, INotification, Mediator } from 'clean-architecture-kernel';
class UserCreatedNotification implements INotification {
constructor(public readonly userId: string, public readonly email: string) {}
}
class SendWelcomeEmailHandler extends NotificationHandler<UserCreatedNotification> {
async handle(notification: UserCreatedNotification) {
console.log(`Sending welcome email to ${notification.email}`);
// Send email logic
}
}
class LogUserCreationHandler extends NotificationHandler<UserCreatedNotification> {
async handle(notification: UserCreatedNotification) {
console.log(`User created: ${notification.userId}`);
// Logging logic
}
}
// Multiple handlers subscribe to the same notification
const mediator = new Mediator();
mediator.subscribeNotification('UserCreatedNotification', new SendWelcomeEmailHandler());
mediator.subscribeNotification('UserCreatedNotification', new LogUserCreationHandler());
// Publish to all handlers (executes in parallel)
await mediator.publish(new UserCreatedNotification('123', '[email protected]'));Pipeline Behaviors - Cross-Cutting Concerns
Add logging, metrics, and other behaviors to your request pipeline:
import { PipelineBehavior, IRequest } from 'clean-architecture-kernel';
class LoggingBehavior<TRequest extends IRequest<TResponse>, TResponse>
extends PipelineBehavior<TRequest, TResponse>
{
async handle(request: TRequest, next: () => Promise<TResponse>): Promise<TResponse> {
console.log(`[${new Date().toISOString()}] Handling ${request.constructor.name}`);
try {
const result = await next();
console.log(`[SUCCESS] ${request.constructor.name} completed`);
return result;
} catch (error) {
console.error(`[ERROR] ${request.constructor.name} failed:`, error);
throw error;
}
}
}
class PerformanceBehavior<TRequest extends IRequest<TResponse>, TResponse>
extends PipelineBehavior<TRequest, TResponse>
{
async handle(request: TRequest, next: () => Promise<TResponse>): Promise<TResponse> {
const start = Date.now();
const result = await next();
const duration = Date.now() - start;
console.log(`${request.constructor.name} took ${duration}ms`);
return result;
}
}
// Register behaviors (they execute in the order added)
const mediator = new Mediator();
mediator.registerBehavior('CreateUserCommand', new LoggingBehavior());
mediator.registerBehavior('CreateUserCommand', new PerformanceBehavior());
mediator.registerHandler('CreateUserCommand', new CreateUserHandler());
// Behaviors wrap the handler execution
const user = await mediator.send(new CreateUserCommand('[email protected]'));Roadmap
- [x] Request validation with Validators
- [x] Notification pattern for pub/sub messaging
- [x] Pipeline behaviors (logging, validation, metrics)
- [ ] Async domain event dispatcher with saga pattern
- [ ] Optional integrations (Redis, Kafka, RabbitMQ)
- [ ] Advanced aggregate query patterns
- [ ] Built-in error handling middleware
- [ ] Request/response compression behaviors
Contributing
Contributions are welcome! To contribute:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Please ensure your code follows the existing style and includes appropriate tests.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Made with ❤️ by Andrés Mariño
For developers building scalable, maintainable architectures
