Skip to content

Commit

Permalink
chore: initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
4lessandrodev committed Mar 6, 2021
0 parents commit 0b3371b
Show file tree
Hide file tree
Showing 28 changed files with 6,697 additions and 0 deletions.
25 changes: 25 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'prettier/@typescript-eslint',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
34 changes: 34 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# compiled output
/dist
/node_modules

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# OS
.DS_Store

# Tests
/coverage
/.nyc_output

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
4 changes: 4 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"singleQuote": true,
"trailingComma": "all"
}
73 changes: 73 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<p align="center">
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo_text.svg" width="320" alt="Nest Logo" /></a>
</p>

[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest

<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
<p align="center">
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a>
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
</p>
<!--[![Backers on Open Collective](https://opencollective.com/nest/backers/badge.svg)](https://opencollective.com/nest#backer)
[![Sponsors on Open Collective](https://opencollective.com/nest/sponsors/badge.svg)](https://opencollective.com/nest#sponsor)-->

## Description

[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.

## Installation

```bash
$ npm install
```

## Running the app

```bash
# development
$ npm run start

# watch mode
$ npm run start:dev

# production mode
$ npm run start:prod
```

## Test

```bash
# unit tests
$ npm run test

# e2e tests
$ npm run test:e2e

# test coverage
$ npm run test:cov
```

## Support

Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).

## Stay in touch

- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)

## License

Nest is [MIT licensed](LICENSE).
41 changes: 41 additions & 0 deletions domain/shared/aggregate-root.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Entity } from './';
import { IDomainEvent } from './';
import { DomainEvents } from './';
import { UniqueEntityID } from './';

export abstract class AggregateRoot<T> extends Entity<T> {
private _domainEvents: IDomainEvent[] = [];

get id(): UniqueEntityID {
return this._id;
}

get domainEvents(): IDomainEvent[] {
return this._domainEvents;
}

protected addDomainEvent(domainEvent: IDomainEvent): void {
// Add the domain event to this aggregate's list of domain events
this._domainEvents.push(domainEvent);
// Add this aggregate instance to the domain event's list of aggregates who's
// events it eventually needs to dispatch.
DomainEvents.markAggregateForDispatch(this);
// Log the domain event
this.logDomainEventAdded(domainEvent);
}

public clearEvents(): void {
this._domainEvents.splice(0, this._domainEvents.length);
}

private logDomainEventAdded(domainEvent: IDomainEvent): void {
const thisClass = Reflect.getPrototypeOf(this);
const domainEventClass = Reflect.getPrototypeOf(domainEvent);
console.info(
'[Domain Event Created]:',
thisClass.constructor.name,
'==>',
domainEventClass.constructor.name,
);
}
}
13 changes: 13 additions & 0 deletions domain/shared/base-domain-entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export abstract class BaseDomainEntity {
constructor(
public createdAt?: Date,
public updatedAt?: Date,
public isDeleted?: boolean,
public deletedAt?: Date,
) {
this.createdAt = createdAt ?? new Date();
this.updatedAt = updatedAt ?? new Date();
this.isDeleted = isDeleted ?? false;
this.deletedAt = isDeleted ? deletedAt : null;
}
}
48 changes: 48 additions & 0 deletions domain/shared/entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { BaseDomainEntity } from './';
import { UniqueEntityID } from './';

const isEntity = (v: any): v is Entity<any> => {
return v instanceof Entity;
};

export abstract class Entity<T extends BaseDomainEntity> {
protected readonly _id: UniqueEntityID;
protected readonly props: T;

constructor(props: T, id?: UniqueEntityID) {
this._id = id ? id : new UniqueEntityID();
this.props = props;
}

get createdAt(): Date {
return this.props.createdAt ?? new Date();
}

get updatedAt(): Date {
return this.props.updatedAt ?? new Date();
}

get deletedAt(): Date | null {
return this.props.deletedAt;
}

get isDeleted(): boolean {
return this.props.isDeleted ?? false;
}

public equals(object?: Entity<T>): boolean {
if (object == null || object == undefined) {
return false;
}

if (this === object) {
return true;
}

if (!isEntity(object)) {
return false;
}

return this._id.equals(object._id);
}
}
6 changes: 6 additions & 0 deletions domain/shared/events/IDomainEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { UniqueEntityID } from '../';

export interface IDomainEvent {
dateTimeOccurred: Date;
getAggregateId(): UniqueEntityID;
}
114 changes: 114 additions & 0 deletions domain/shared/events/domain-events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/* eslint-disable @typescript-eslint/no-extraneous-class */
/* eslint-disable */

import { AggregateRoot } from '../';
import { UniqueEntityID } from '../';
import { IDomainEvent } from '../';

export class DomainEvents {
private static handlersMap: any = {};
private static markedAggregates: AggregateRoot<any>[] = [];

/**
* @method markAggregateForDispatch
* @static
* @desc Called by aggregate root objects that have created domain
* events to eventually be dispatched when the infrastructure commits
* the unit of work.
*/

public static markAggregateForDispatch(
aggregate: AggregateRoot<any>,
): void {
const aggregateFound = !!this.findMarkedAggregateByID(aggregate.id);

if (!aggregateFound) {
this.markedAggregates.push(aggregate);
}
}

private static dispatchAggregateEvents(
aggregate: AggregateRoot<any>,
): void {
this.logDomainEventDispatch(aggregate);

aggregate.domainEvents.forEach((event: IDomainEvent) =>
this.dispatch(event),
);
}

private static logDomainEventDispatch(
aggregate: AggregateRoot<any>,
): void {
const thisClass = Reflect.getPrototypeOf(this);
const domainEventClass = Reflect.getPrototypeOf(aggregate);

console.info(
'[Domain Event Dispatched]:',
thisClass.constructor.name,
'==>',
domainEventClass.constructor.name,
);
}
private static removeAggregateFromMarkedDispatchList(
aggregate: AggregateRoot<any>,
): void {
// console.log('removing aggreagete from marked', JSON.stringify(aggregate));
const index = this.markedAggregates.findIndex((a) =>
a.equals(aggregate),
);
this.markedAggregates.splice(index, 1);
}

private static findMarkedAggregateByID(
id: UniqueEntityID,
): AggregateRoot<any> | null {
let found = null;
for (let aggregate of this.markedAggregates) {
if (aggregate.id.equals(id)) {
found = aggregate;
}
}

return found;
}

public static dispatchEventsForAggregate(id: UniqueEntityID): void {
const aggregate = this.findMarkedAggregateByID(id);

if (aggregate) {
this.dispatchAggregateEvents(aggregate);
aggregate.clearEvents();
this.removeAggregateFromMarkedDispatchList(aggregate);
}
}

public static register(
callback: (event: IDomainEvent) => void,
eventClassName: string,
): void {
if (!this.handlersMap.hasOwnProperty(eventClassName)) {
this.handlersMap[eventClassName] = [];
}
this.handlersMap[eventClassName].push(callback);
}

public static clearHandlers(): void {
this.handlersMap = {};
}

public static clearMarkedAggregates(): void {
this.markedAggregates = [];
}

private static dispatch(event: IDomainEvent): void {
const eventClassName: string = event.constructor.name;

if (this.handlersMap.hasOwnProperty(eventClassName)) {
const handlers: any[] = this.handlersMap[eventClassName];
for (let handler of handlers) {
handler(event);
}
}
}
}
5 changes: 5 additions & 0 deletions domain/shared/events/ihandle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { IDomainEvent } from '../';

export interface IHandle<IDomainEvent> {
setupSubscriptions(): void;
}
Loading

0 comments on commit 0b3371b

Please sign in to comment.