npm package: @sisques-labs/core
Description: Core library with common interfaces, DTOs, and domain patterns
Author: Sisques Labs
Latest version: 1.1.0

# @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**

- `Criteria` for complex queries with filters, sorting, and pagination
- `PaginatedResult` for paginated responses

### 🎯 Application Layer

**Services**

- `IBaseService` interface for application services
- `PublishIntegrationEventsService` for publishing integration events
- `PublishDomainEventsService` for publishing domain events

**Command Handlers**

- `IBaseCommandHandler` interface
- `BaseUpdateCommandHandler` with utilities for extracting update data

**Sagas**

- `IBaseSaga` interface for orchestrating distributed transactions
- `ISagaStepConfig` for saga step configuration

**DTOs**

- Command DTOs like `IBaseFieldChangedCommandDto`

### 🔧 Infrastructure Layer

**Repositories**

- `BaseDatabaseRepository` with common utilities (pagination calculation, etc.)

**Exceptions**

- Layered exception hierarchy (Domain, Application, Infrastructure)

## Installation

```bash
# Using pnpm (recommended)
pnpm add @sisques-labs/core

# Using npm
npm install @sisques-labs/core

# Using yarn
yarn add @sisques-labs/core
```

## Usage Examples

### Value Objects

```typescript
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('user@example.com');
```

### Builders

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
import {
	Criteria,
	FilterOperator,
	SortDirection,
} from '@sisques-labs/core';

const criteria = new Criteria(
	[
		{
			field: 'email',
			operator: FilterOperator.EQUALS,
			value: 'user@example.com',
		},
		{ 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 pages
```

## Architecture

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 file
```

## Framework 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

```bash
# 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 clean
```

## Code Quality

This project uses [ESLint](https://eslint.org/) and [Prettier](https://prettier.io/) to maintain code quality and consistency.

### Linting

```bash
# Check for linting issues
pnpm lint

# Automatically fix linting issues
pnpm lint:fix
```

The project uses ESLint with TypeScript support and Prettier integration. Configuration can be found in `eslint.config.mjs`.

### Formatting

```bash
# Format all code
pnpm format

# Check if code is formatted correctly
pnpm format:check
```

Prettier configuration is defined in `.prettierrc` and follows the project's coding standards.

## Testing

This library uses [Jest](https://jestjs.io/) for unit testing. All value objects, entities, and core classes have comprehensive test coverage.

```bash
# Run all tests
pnpm test

# Run tests in watch mode (useful during development)
pnpm test:watch

# Generate coverage report
pnpm test:coverage
```

Test files follow the naming convention `*.spec.ts` and are located alongside the source files.

## Git Hooks

This project uses [Husky](https://typicode.github.io/husky/) to run Git hooks that ensure code quality before commits.

### Pre-commit Hook

Before each commit, the following checks are automatically performed:

1. **Linting & Formatting**: All staged files are automatically linted and formatted using ESLint and Prettier
2. **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:

```bash
git commit --no-verify
```

However, this should only be used in exceptional circumstances.

## Versioning and Releases

This project uses [Semantic Release](https://semantic-release.gitbook.io/) for automated versioning and publishing. The version is automatically determined based on commit messages following the [Conventional Commits](https://www.conventionalcommits.org/) 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 changes
- `style`: Code style changes (formatting, etc.)
- `refactor`: Code refactoring
- `perf`: Performance improvements
- `test`: Adding or updating tests
- `chore`: Maintenance tasks
- `BREAKING CHANGE`: Breaking changes (triggers a major version bump)

**Examples:**
```bash
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 builders
```

### Release Process

1. **Automatic Release**: When code is pushed to the `main` branch, 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

2. **Manual Release**: You can also run semantic-release manually:
   ```bash
   pnpm release
   ```

### Configuration

Semantic-release configuration is in `.releaserc.json`. The release process requires:
- `GITHUB_TOKEN`: Automatically provided by GitHub Actions
- `NPM_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