@sisques-labs/core
v1.1.0
Published
Core library with common interfaces, DTOs, and domain patterns
Maintainers
Readme
@sisques-labs/core
A framework-agnostic TypeScript library providing common domain patterns, interfaces, and utilities for building microservices following Domain-Driven Design (DDD) principles.
What is this?
@sisques-labs/core is a foundational library designed to standardize development patterns across microservices. It provides a comprehensive set of abstractions, interfaces, and base classes that enable consistent implementation of domain-driven design patterns without coupling your code to any specific framework (like NestJS, Express, etc.).
Purpose
This library serves as the shared foundation for all microservices, providing:
- Consistency: Standardized patterns and interfaces across all services
- Flexibility: Framework-agnostic design allows you to use any backend framework
- Reusability: Common domain patterns, value objects, and utilities
- Type Safety: Full TypeScript support with comprehensive type definitions
- Maintainability: Centralized core logic that can be updated across all services
Key Features
🏗️ Domain Layer
Interfaces & Patterns
- Builder patterns (
IBuilder,IReadBuilder,IWriteBuilder) - Factory patterns (
IReadFactory,IWriteFactory) - Repository patterns (
IBaseReadRepository,IBaseWriteRepository) - DTOs for aggregates and view models
Event System
- Domain events (
IDomainEvent) for within bounded context communication - Integration events (
BaseEvent) for cross-bounded context communication - Event publishers with abstraction for different transport mechanisms
Value Objects
- Common value objects:
UuidValueObject,DateValueObject,EmailValueObject,PhoneValueObject, etc. - Type-safe validation and encapsulation
- Comprehensive exception handling
Domain Entities
Criteriafor complex queries with filters, sorting, and paginationPaginatedResultfor paginated responses
🎯 Application Layer
Services
IBaseServiceinterface for application servicesPublishIntegrationEventsServicefor publishing integration eventsPublishDomainEventsServicefor publishing domain events
Command Handlers
IBaseCommandHandlerinterfaceBaseUpdateCommandHandlerwith utilities for extracting update data
Sagas
IBaseSagainterface for orchestrating distributed transactionsISagaStepConfigfor saga step configuration
DTOs
- Command DTOs like
IBaseFieldChangedCommandDto
🔧 Infrastructure Layer
Repositories
BaseDatabaseRepositorywith common utilities (pagination calculation, etc.)
Exceptions
- Layered exception hierarchy (Domain, Application, Infrastructure)
Installation
# Using pnpm (recommended)
pnpm add @sisques-labs/core
# Using npm
npm install @sisques-labs/core
# Using yarn
yarn add @sisques-labs/coreUsage Examples
Value Objects
import {
UuidValueObject,
DateValueObject,
EmailValueObject,
} from '@sisques-labs/core';
// Create a UUID
const userId = new UuidValueObject();
const existingId = new UuidValueObject('123e4567-e89b-12d3-a456-426614174000');
// Create a date
const createdAt = new DateValueObject();
const specificDate = new DateValueObject(new Date('2024-01-01'));
// Create an email with validation
const email = new EmailValueObject('[email protected]');Builders
import { IReadBuilder, IWriteBuilder } from '@sisques-labs/core';
// Read Builder for View Models
class UserViewModelBuilder implements IReadBuilder<
UserViewModel,
UserPrimitives
> {
private viewModel: Partial<UserViewModel> = {};
withId(id: string): this {
this.viewModel.id = id;
return this;
}
withName(name: string): this {
this.viewModel.name = name;
return this;
}
fromPrimitives(primitives: UserPrimitives): this {
this.viewModel = { ...primitives };
return this;
}
build(): UserViewModel {
return this.viewModel as UserViewModel;
}
reset(): this {
this.viewModel = {};
return this;
}
}
// Write Builder for Aggregates
class UserAggregateBuilder implements IWriteBuilder<
UserAggregate,
UserPrimitives
> {
// Similar implementation for domain aggregates
}Repositories
import {
IBaseReadRepository,
IBaseWriteRepository,
Criteria,
PaginatedResult,
} from '@sisques-labs/core';
// Read Repository
class UserReadRepository implements IBaseReadRepository<UserViewModel> {
async findById(id: string): Promise<UserViewModel | null> {
// Implementation
}
async findByCriteria(
criteria: Criteria,
): Promise<PaginatedResult<UserViewModel>> {
// Implementation with filters, sorting, pagination
}
async save(viewModel: UserViewModel): Promise<void> {
// Implementation
}
async delete(id: string): Promise<void> {
// Implementation
}
}
// Write Repository
class UserWriteRepository implements IBaseWriteRepository<UserAggregate> {
async findById(id: string): Promise<UserAggregate | null> {
// Implementation
}
async save(entity: UserAggregate): Promise<UserAggregate> {
// Implementation
}
async delete(id: string): Promise<void> {
// Implementation
}
}Events
import {
BaseEvent,
IDomainEvent,
IEventMetadata,
IBaseEventData,
IIntegrationEventPublisher,
} from '@sisques-labs/core';
// Integration Event (cross-bounded context)
interface UserCreatedEventData extends IBaseEventData {
userId: string;
userName: string;
email: string;
}
class UserCreatedEvent extends BaseEvent<UserCreatedEventData> {
constructor(metadata: IEventMetadata, data: UserCreatedEventData) {
super(metadata, data);
}
}
// Domain Event (within bounded context)
const userRegisteredEvent: IDomainEvent<{ userId: string }> = {
eventType: 'UserRegistered',
data: { userId: '123' },
occurredOn: new Date(),
};Application Services
import {
IBaseService,
PublishIntegrationEventsService,
} from '@sisques-labs/core';
// Implement a service
class CreateUserService implements IBaseService<CreateUserCommand, User> {
constructor(
private userRepository: IBaseWriteRepository<User>,
private eventPublisher: PublishIntegrationEventsService,
) {}
async execute(command: CreateUserCommand): Promise<User> {
const user = User.create(command);
await this.userRepository.save(user);
// Publish integration event
const event = new UserCreatedEvent(metadata, eventData);
await this.eventPublisher.execute(event);
return user;
}
}Command Handlers
import { BaseUpdateCommandHandler } from '@sisques-labs/core';
class UpdateUserHandler extends BaseUpdateCommandHandler<
UpdateUserCommand,
UserUpdateDto
> {
async handle(command: UpdateUserCommand) {
// Extract update data, excluding 'id' field
const updateData = this.extractUpdateData(command, ['id']);
// Or with transformation
const transformedData = this.extractUpdateDataWithTransform(
command,
(data) => ({ ...data, updatedAt: new Date() }),
['id'],
);
// Use updateData to update the entity
}
}Database Repositories
import { BaseDatabaseRepository, Criteria } from '@sisques-labs/core';
class UserRepository extends BaseDatabaseRepository {
async findByCriteria(criteria: Criteria) {
// Use built-in pagination utility
const { page, limit, skip } = this.calculatePagination(criteria);
// Apply filters, sorting, and pagination
// Implementation with your database client
}
}Querying with Criteria
import {
Criteria,
FilterOperator,
SortDirection,
} from '@sisques-labs/core';
const criteria = new Criteria(
[
{
field: 'email',
operator: FilterOperator.EQUALS,
value: '[email protected]',
},
{ field: 'age', operator: FilterOperator.GREATER_THAN, value: 18 },
],
[{ field: 'createdAt', direction: SortDirection.DESC }],
{ page: 1, perPage: 20 },
);
const result = await userRepository.findByCriteria(criteria);
// result.items - array of users
// result.total - total count
// result.page - current page
// result.perPage - items per page
// result.totalPages - calculated total pagesArchitecture
This library follows Domain-Driven Design (DDD) principles with a clear separation of concerns:
src/
├── domain/ # Domain Layer - Business Logic
│ ├── interfaces/ # Domain contracts and interfaces
│ ├── entities/ # Domain entities (Criteria, PaginatedResult)
│ ├── events/ # Event classes (BaseEvent)
│ ├── enums/ # Domain enumerations
│ ├── exceptions/ # Domain-specific exceptions
│ └── value-objects/ # Value objects with validation
│
├── application/ # Application Layer - Use Cases
│ ├── interfaces/ # Service and handler interfaces
│ ├── services/ # Application services
│ ├── commands/ # Command handlers
│ ├── sagas/ # Saga orchestration
│ ├── dtos/ # Data transfer objects
│ └── exceptions/ # Application exceptions
│
├── infrastructure/ # Infrastructure Layer - Technical Details
│ ├── database/ # Database abstractions
│ └── exceptions/ # Infrastructure exceptions
│
└── index.ts # Main export fileFramework Agnostic
This library is designed to work with any backend framework:
- ✅ NestJS
- ✅ Express
- ✅ Fastify
- ✅ Koa
- ✅ Custom frameworks
The interfaces and abstractions allow you to implement the patterns using your preferred framework while maintaining consistency across all services.
TypeScript Support
Full TypeScript support with:
- Comprehensive type definitions
- Generic types for flexibility
- Strict type checking
- IntelliSense support
Development
# Install dependencies
pnpm install
# Build the library
pnpm build
# Watch mode for development
pnpm build:watch
# Run linter
pnpm lint
# Fix linting issues automatically
pnpm lint:fix
# Format code with Prettier
pnpm format
# Check code formatting
pnpm format:check
# Run tests
pnpm test
# Run tests in watch mode
pnpm test:watch
# Run tests with coverage
pnpm test:coverage
# Clean build artifacts
pnpm cleanCode Quality
This project uses ESLint and Prettier to maintain code quality and consistency.
Linting
# Check for linting issues
pnpm lint
# Automatically fix linting issues
pnpm lint:fixThe project uses ESLint with TypeScript support and Prettier integration. Configuration can be found in eslint.config.mjs.
Formatting
# Format all code
pnpm format
# Check if code is formatted correctly
pnpm format:checkPrettier configuration is defined in .prettierrc and follows the project's coding standards.
Testing
This library uses Jest for unit testing. All value objects, entities, and core classes have comprehensive test coverage.
# Run all tests
pnpm test
# Run tests in watch mode (useful during development)
pnpm test:watch
# Generate coverage report
pnpm test:coverageTest files follow the naming convention *.spec.ts and are located alongside the source files.
Git Hooks
This project uses Husky to run Git hooks that ensure code quality before commits.
Pre-commit Hook
Before each commit, the following checks are automatically performed:
- Linting & Formatting: All staged files are automatically linted and formatted using ESLint and Prettier
- Tests: All unit tests are executed to ensure nothing is broken
If any of these checks fail, the commit will be aborted. This ensures that only properly formatted and tested code enters the repository.
Bypassing Hooks (Not Recommended)
If you need to bypass the hooks in an emergency (not recommended), you can use:
git commit --no-verifyHowever, this should only be used in exceptional circumstances.
Versioning and Releases
This project uses Semantic Release for automated versioning and publishing. The version is automatically determined based on commit messages following the Conventional Commits specification.
Commit Message Format
Commit messages must follow the Conventional Commits format:
<type>(<scope>): <subject>
<body>
<footer>Types:
feat: A new feature (triggers a minor version bump)fix: A bug fix (triggers a patch version bump)docs: Documentation only changesstyle: Code style changes (formatting, etc.)refactor: Code refactoringperf: Performance improvementstest: Adding or updating testschore: Maintenance tasksBREAKING CHANGE: Breaking changes (triggers a major version bump)
Examples:
feat: add new value object for phone numbers
fix: correct UUID validation regex
feat(domain): add new repository interface
BREAKING CHANGE: change API signature for buildersRelease Process
Automatic Release: When code is pushed to the
mainbranch, semantic-release:- Analyzes commits since the last release
- Determines the next version number
- Generates release notes
- Updates
CHANGELOG.md - Creates a Git tag
- Publishes to npm (if configured)
- Creates a GitHub release
Manual Release: You can also run semantic-release manually:
pnpm release
Configuration
Semantic-release configuration is in .releaserc.json. The release process requires:
GITHUB_TOKEN: Automatically provided by GitHub ActionsNPM_TOKEN: Required for publishing to npm (set in repository secrets)
Contributing
This is an internal library. For contributions, please follow the project's contribution guidelines.
Important: When contributing, ensure your commit messages follow the Conventional Commits format to enable proper automatic versioning.
License
MIT © Sisques Labs
