@datadom/core
TypeScript icon, indicating that this package has built-in type declarations

0.1.7 • Public • Published

@datadom/core

This package contains the core elements of Datadom. These include domains, models, and rules.

For more info you can visit the project wiki.

Installation

npm i @datadom/core

Domain

This represents the problem space your project occupies and provides a solution to.

import { Domain } from '@datadom/core';

const domain = new Domain();

Read more ➡️

Models

Domain models map to your business entities.

Datadom does not provide specific facilities for representing domain models. There is no interface to implement or class to inherit.

Read more ➡️

Repositories

A repository is an object that represents a collection of models. Typically, a repository would be responsible for CRUD (Create Read Update Delete) operations on models.

import { ID, IRepository, Persisted } from '@datadom/core';

interface ICharacter {
  id?: ID;
  name: string;
  createdAt?: Date;
  updatedAt?: Date;
}

interface ICharacterRepository extends IRepository<ICharacter> {}

export class CharacterRepository implements ICharacterRepository {
    count(params?: IQueryBuilder<ICharacter>): Promise<number> {
        throw new Error('Method not implemented.');
    }
    delete(id: ID): Promise<OperationResult> {
        throw new Error('Method not implemented.');
    }
    deleteMany(params: IQueryBuilder<ICharacter>): Promise<OperationResult> {
        throw new Error('Method not implemented.');
    }
    exists(params: IQueryBuilder<ICharacter>): Promise<boolean> {
        throw new Error('Method not implemented.');
    }
    get(id: ID): Promise<Persisted<ICharacter> | null> {
        throw new Error('Method not implemented.');
    }
    getMany(params?: IQueryBuilder<ICharacter>): Promise<Persisted<ICharacter>[]> {
        throw new Error('Method not implemented.');
    }
    save(data: SaveInput<ICharacter>): Promise<Persisted<ICharacter>> {
        throw new Error('Method not implemented.');
    }
    update(id: ID, data: UpdateInput<ICharacter>): Promise<OperationResult> {
        throw new Error('Method not implemented.');
    }
    updateMany(params: IQueryBuilder<ICharacter>, data: UpdateInput<ICharacter>): Promise<OperationResult> {
        throw new Error('Method not implemented.');
    }
}

Read more ➡️

Services

A service is a wrapper around a repository.

Whenever you register a repository in the domain by calling domain.registerRepository, a service of the same name is created under the hood. This service exposes the same methods exposed by the repository. However, a service ensures that the relevant rules, events, and middleware are run before and after certain repository actions.

import { Domain } from '@datadom/core';
import { CharacterRepository } from './characters';

const domain = new Domain();

domain.registerRepository('character', new CharacterRepository());

It is, however, possible to create a service without a concrete repository by calling domain.createService. This way, you can attach all the domain rules, hooks, and event listeners to the service without worrying about the actual repository that will be associated with the service.

import { Domain } from '@datadom/core';

const domain = new Domain();

domain.createService('character');

Services created this way are associated with a default repository of type NullRepository.

To associated a repository to a service created this way, simply call domain.registerRepository. The NullRepository repository will be replaced with the provided repository, and all the service's previously-added rules, hooks, and event listeners will continue to function as expected. This is great for ensuring domain code remains decoupled from infrastructure code.

domain.registerRepository('character', new CharacterRepository());

After registering a repository with a domain, you can access the wrapping service in a number of ways, depending on how strict your type-checking is.

// access the character service using any of the following notations:

domain.$('character');
(domain as any).$character;
(domain as any)['$character'];
domain.$character; // without strict type-checking
domain['$character']; // without strict type-checking

You can attach various hooks, rules, and event listeners to a service to perform actions before and after various repository operations. This helps the repository methods to concerned with only their tasks and not have to worry about preconditions, checks, and side effects.

domain.$('character').addRule('save', (data) => {
  console.log('This rule runs before an entity is saved by the repository');

  // ensure that the entity to be saved has a "name" field
  return !!(data?.name);
});
domain.$('character').pre('save', (data, next) => {
  console.log('This hook/middleware runs before an entity is saved by the repository');

  // call "next" to continue to the next middleware in the chain
  // you can call "next" like this "next(data)" or simply like this "next()"
  return next();
});
domain.$('character').pre('save', (data, next) => {
  console.log('This hook/middleware runs before an entity is saved by the repository');

  // call "next" to continue to the next middleware in the chain
  // you can call "next" like this "next(data)" or simply like this "next()"
  return next(data);
});
domain.$('character').pre('save', (data, next) => {
  console.log('This hook/middleware alters the entity that is to be saved by the repository');

  return next({ ...data, field1: 'This field was added in a middleware' });
});
domain.$('character').on('save', () => console.log('This event handler runs after an entity is saved by the repository'));

Read more ➡️

Rules

Rules are functions that resolve to boolean values and are executed before repository operations. A resolved value of false means the rule is violated, and the repository operation should not continue.

Rules can be attached to a specific service or to the domain object, in which case they are available to every service in the domain.

import { Domain } from '@datadom/core';

const domain = new Domain();

async function entityMustHaveId(data) {
  return !!(data?.id);
}

domain.addRule('save', entityMustHaveId);

The rule entityMustHaveId will be executed whenever a repository wants to save an entity.

Read more ➡️

Package Sidebar

Install

npm i @datadom/core

Weekly Downloads

183

Version

0.1.7

License

MIT

Unpacked Size

78.2 kB

Total Files

62

Last publish

Collaborators

  • chieze-franklin