-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 0b3371b
Showing
28 changed files
with
6,697 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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', | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"singleQuote": true, | ||
"trailingComma": "all" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { UniqueEntityID } from '../'; | ||
|
||
export interface IDomainEvent { | ||
dateTimeOccurred: Date; | ||
getAggregateId(): UniqueEntityID; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { IDomainEvent } from '../'; | ||
|
||
export interface IHandle<IDomainEvent> { | ||
setupSubscriptions(): void; | ||
} |
Oops, something went wrong.