Skip to content

Commit af238b6

Browse files
committed
Added several use cases and restructured project.
1 parent 1916306 commit af238b6

File tree

107 files changed

+1004
-187
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

107 files changed

+1004
-187
lines changed

package-lock.json

+43
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"description": "A Vinyl-Trading enterprise app built with Node.js + TypeScript using Domain-Driven Design",
55
"main": "bin/www",
66
"dependencies": {
7+
"axios": "^0.19.0",
78
"bcrypt-nodejs": "0.0.3",
89
"chai": "^4.2.0",
910
"dotenv": "^8.0.0",
@@ -17,6 +18,7 @@
1718
"typescript": "^3.5.2"
1819
},
1920
"devDependencies": {
21+
"@types/axios": "^0.14.0",
2022
"@types/bcrypt-nodejs": "0.0.30",
2123
"@types/jest": "^24.0.15",
2224
"cross-env": "^5.2.0",

src/core/Result.ts

-42
This file was deleted.

src/core/logic/AppError.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
2+
import { Result } from "./Result";
3+
import { UseCaseError } from "./UseCaseError";
4+
5+
export namespace GenericAppError {
6+
export class UnexpectedError extends Result<UseCaseError> {
7+
public constructor (err: any) {
8+
super(false, {
9+
message: `An unexpected error occurred.`,
10+
error: err
11+
} as UseCaseError)
12+
console.log(`[AppError]: An unexpected error occurred`);
13+
console.error(err);
14+
}
15+
16+
public static create (err: any): UnexpectedError {
17+
return new UnexpectedError(err);
18+
}
19+
}
20+
}
File renamed without changes.

src/core/logic/Result.ts

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
2+
export class Result<T> {
3+
public isSuccess: boolean;
4+
public isFailure: boolean
5+
public error: T | string;
6+
private _value: T;
7+
8+
public constructor (isSuccess: boolean, error?: T | string, value?: T) {
9+
if (isSuccess && error) {
10+
throw new Error("InvalidOperation: A result cannot be successful and contain an error");
11+
}
12+
if (!isSuccess && !error) {
13+
throw new Error("InvalidOperation: A failing result needs to contain an error message");
14+
}
15+
16+
this.isSuccess = isSuccess;
17+
this.isFailure = !isSuccess;
18+
this.error = error;
19+
this._value = value;
20+
21+
Object.freeze(this);
22+
}
23+
24+
public getValue () : T {
25+
if (!this.isSuccess) {
26+
console.log(this.error,);
27+
throw new Error("Can't get the value of an error result. Use 'errorValue' instead.")
28+
}
29+
30+
return this._value;
31+
}
32+
33+
public errorValue (): T {
34+
return this.error as T;
35+
}
36+
37+
public static ok<U> (value?: U) : Result<U> {
38+
return new Result<U>(true, null, value);
39+
}
40+
41+
public static fail<U> (error: any): Result<U> {
42+
return new Result<U>(false, error);
43+
}
44+
45+
public static combine (results: Result<any>[]) : Result<any> {
46+
for (let result of results) {
47+
if (result.isFailure) return result;
48+
}
49+
return Result.ok();
50+
}
51+
}
52+
53+
export type Either<L, A> = Left<L, A> | Right<L, A>;
54+
55+
export class Left<L, A> {
56+
readonly value: L;
57+
58+
constructor(value: L) {
59+
this.value = value;
60+
}
61+
62+
isLeft(): this is Left<L, A> {
63+
return true;
64+
}
65+
66+
isRight(): this is Right<L, A> {
67+
return false;
68+
}
69+
}
70+
71+
export class Right<L, A> {
72+
readonly value: A;
73+
74+
constructor(value: A) {
75+
this.value = value;
76+
}
77+
78+
isLeft(): this is Left<L, A> {
79+
return false;
80+
}
81+
82+
isRight(): this is Right<L, A> {
83+
return true;
84+
}
85+
}
86+
87+
export const left = <L, A>(l: L): Either<L, A> => {
88+
return new Left(l);
89+
};
90+
91+
export const right = <L, A>(a: A): Either<L, A> => {
92+
return new Right<L, A>(a);
93+
};

src/core/logic/UseCaseError.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
interface IUseCaseErrorError {
3+
message: string;
4+
}
5+
6+
export abstract class UseCaseError implements IUseCaseErrorError {
7+
public readonly message: string;
8+
9+
constructor (message: string) {
10+
this.message = message;
11+
}
12+
}

src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
import "./catalog"
1+
import "./modules/vinyl"
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

src/infra/sequelize/models/index.ts

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
2+
/*
3+
* app/models/index.js
4+
*
5+
* Loads all models and creates the relationships for those models and whatnot.
6+
* Date: 2018/02/05
7+
*
8+
* v1.0.0 - 2018/11/26 - Added co-op filters to AudienceFilters
9+
*/
10+
11+
import * as fs from 'fs'
12+
import * as path from 'path'
13+
import config from '../config/config'
14+
import * as Sequelize from 'sequelize'
15+
16+
17+
const sequelize = config.db.conn;
18+
const { connectionStatus } = config.db
19+
20+
// turns base_user => BaseUser
21+
function toCamelCase (str) {
22+
const _ = str.indexOf("_");
23+
if (~_) {
24+
return toCamelCase(str.substring(0, _)
25+
+ str.substring(_ + 1)
26+
.substring(0, 1)
27+
.toUpperCase()
28+
+ str.substring(_ + 2)
29+
)
30+
}
31+
else {
32+
return str.substring(0, 1).toUpperCase() + str.substring(1);
33+
}
34+
}
35+
36+
let models: any = {};
37+
let modelsLoaded = false;
38+
39+
const createModels = () => {
40+
if (modelsLoaded) return models;
41+
42+
// Get all models
43+
const modelsList = fs.readdirSync(path.resolve(__dirname, "./"))
44+
.filter((t) => ~t.indexOf('.js') && !~t.indexOf("index") && !~t.indexOf(".map"))
45+
.map((model) => sequelize.import(__dirname + '/' + model))
46+
47+
// Camel case the models
48+
for (let i = 0; i < modelsList.length; i++) {
49+
const modelName = toCamelCase(modelsList[i].name);
50+
models[modelName] = modelsList[i];
51+
}
52+
53+
// Create the relationships for the models;
54+
Object.keys(models).forEach((modelName) => {
55+
if (models[modelName].associate) {
56+
models[modelName].associate(models);
57+
}
58+
});
59+
60+
models['sequelize'] = sequelize;
61+
models['Sequelize'] = Sequelize;
62+
63+
modelsLoaded = true;
64+
65+
return models;
66+
}
67+
68+
export default createModels();
69+
70+
export function getModels () : Promise<any> {
71+
return new Promise((resolve, reject) => {
72+
connectionStatus.subscribe((status: boolean) => {
73+
if (status) {
74+
return resolve(createModels())
75+
}
76+
})
77+
})
78+
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

src/notification/domain/subscription/vinylCreatedSubscription.ts src/modules/notification/domain/subscription/vinylCreatedSubscription.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11

2-
import { IHandle } from "../../../core/domain/events/IHandle";
3-
import { VinylCreatedEvent } from "../../../catalog/domain/events/vinylCreatedEvent";
4-
import { DomainEvents } from "../../../core/domain/events/DomainEvents";
5-
import { IVinylRepo } from "../../../catalog/repos/vinylRepo";
2+
import { IHandle } from "../../../../core/domain/events/IHandle";
3+
import { VinylCreatedEvent } from "../../../vinyl/domain/events/vinylCreatedEvent";
4+
import { DomainEvents } from "../../../../core/domain/events/DomainEvents";
5+
import { IVinylRepo } from "../../../vinyl/repos/vinylRepo";
66

77
export class VinylCreatedSubscription implements IHandle<VinylCreatedEvent> {
88
private vinylRepo: IVinylRepo;

src/trading/domain/Trader.ts src/modules/trading/domain/trader.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

2-
import { AggregateRoot } from "../../core/domain/AggregateRoot";
3-
import { UniqueEntityID } from "../../core/domain/UniqueEntityID";
4-
import { Result } from "../../core/Result";
2+
import { AggregateRoot } from "../../../core/domain/AggregateRoot";
3+
import { UniqueEntityID } from "../../../core/domain/UniqueEntityID";
4+
import { Result } from "../../../core/logic/Result";
55
import { UserId } from "../../users/domain/userId";
66

77
interface TraderProps {

src/trading/domain/traderId.ts src/modules/trading/domain/traderId.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

2-
import { Entity } from "../../core/domain/Entity";
3-
import { UniqueEntityID } from "../../core/domain/UniqueEntityID";
2+
import { Entity } from "../../../core/domain/Entity";
3+
import { UniqueEntityID } from "../../../core/domain/UniqueEntityID";
44

55
export class TraderId extends Entity<any> {
66

File renamed without changes.
File renamed without changes.

src/users/domain/events/userCreatedEvent.ts src/modules/users/domain/events/userCreatedEvent.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { IDomainEvent } from "../../../core/domain/events/IDomainEvent";
2-
import { UniqueEntityID } from "../../../core/domain/UniqueEntityID";
1+
import { IDomainEvent } from "../../../../core/domain/events/IDomainEvent";
2+
import { UniqueEntityID } from "../../../../core/domain/UniqueEntityID";
33
import { User } from "../user";
44

55
export class UserCreatedEvent implements IDomainEvent {

0 commit comments

Comments
 (0)