A set of utilities for Domain-Driven Design
Packages | Link |
---|---|
@ts-ddd/common | Go to package |
@ts-ddd/entity | Go to package |
@ts-ddd/state-machine | Go to package |
@ts-ddd/value-object | Go to package |
import { uuidGenerator } from '@ts-ddd/common';
const uuid = uuidGenerator() // -> b9ebc3ea-bfbd-4a86-8b7b-39995b47c631
Interface | Properties |
---|---|
WithActive | isActive |
WithAudit | createdBy, updatedBy and deletedBy as date |
WithDomain | domain |
WithLocale | locale |
WithNullable | isNullable |
WithOwner | ownerId |
WithScope | scope |
WithStatus | status |
WithTimestamps | createdAt and deletedAt |
WithUser | userId |
WithUuid | uuid |
WithVersion | version |
interface Post extends WithActive, WithUuid {
title: string;
body: string;
}
/*
{
uuid: string;
title: string;
body: string;
isActive: boolean;
}
*/
Use to create domain entities using an interface as contract.
export interface IBook {
id: Identifier;
name: string;
year: number;
publisher: string;
}
import { uuidGenerator } from '@ts-ddd/common';
import { Identifier, Entity } from '@ts-ddd/entity';
import { IBook } from '../interfaces/book.interface';
export class Book extends Entity<IBook> {
public static createToPrimitives(payload: Omit<IBook, 'id'>) {
return new Book({
id: uuidGenerator(),
...payload
})
}
public static createFromPrimitives(payload: IBook): Book {
return new Book(payload)
}
}
class BooksService {
constructor(private readonly booksRepository: BooksRepository) {}
public async findBookById(id: string): Promise<Book | undefined> {
const book = await this.booksRepository.findOne({ id });
if (book) {
return Book.createFromPrimitives(book)
}
return undefined;
}
}
class CreateBookUseCase {
constructor(private readonly booksService: BooksService) {}
public async execute(payload: Omit<IBook, 'id'>): Promise<Book | undefined> {
const book = await this.booksRepository.create(
Book.createToPrimitives(payload)
);
return book;
}
}
import { StateMachine } from '@ts-ddd/state-machine';
enum LightTransitions {
RED = "red",
GREEN = "green",
PULSING_GREEN = "pulsing_green",
AMBER = "amber",
}
const transitions = {
[LightTransitions.RED]: [LightTransitions.GREEN],
[LightTransitions.GREEN]: [LightTransitions.PULSING_GREEN],
[LightTransitions.PULSING_GREEN]: [LightTransitions.AMBER],
[LightTransitions.AMBER]: [LightTransitions.RED],
}
const light = StateMachine.instance({
transitions,
currentState: LightTransitions.RED,
});
try {
light.setState(LightTransitions.GREEN);
} catch (error) {
// InvalidStateTransitionException
}
import { StringValue } from '@ts-ddd/value-object'
import { validate, v4 as uuid } from 'uuid';
export class Uuid extends StringValue {
constructor(value: string) {
super(value);
this.ensureIsValidUuid(value);
}
static generate(): Uuid {
return new Uuid(uuid());
}
private ensureIsValidUuid(value: string): void {
if (!validate(value)) {
throw new Error(`${value} is not a valid UUID`)
}
}
}
import { StringValue } from '@ts-ddd/value-object'
export class Title extends StringValue {}
import { PositiveNumberValue } from '@ts-ddd/value-object'
export class Year extends PositiveNumberValue {}
class BooksRepository {
public async getBook(id: string): Promise<Book | undefined> {
const book = await this.booksRepository.findOne({ id });
if (!book) {
return undefined;
}
return Book.createFromPrimitives({
id: new Uuid(book.id),
title: new Title(book.title),
year: new Year(book.year)
})
}
}