rename to core
This commit is contained in:
3
core/shared/application/AsyncUseCase.ts
Normal file
3
core/shared/application/AsyncUseCase.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface AsyncUseCase<Input, Output> {
|
||||
execute(input: Input): Promise<Output>;
|
||||
}
|
||||
18
core/shared/application/Service.ts
Normal file
18
core/shared/application/Service.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { Result } from '../result/Result';
|
||||
import type { IApplicationError } from '../errors/ApplicationError';
|
||||
|
||||
export interface IApplicationService {
|
||||
readonly serviceName?: string;
|
||||
}
|
||||
|
||||
export interface IAsyncApplicationService<Input, Output> extends IApplicationService {
|
||||
execute(input: Input): Promise<Output>;
|
||||
}
|
||||
|
||||
export interface IAsyncResultApplicationService<
|
||||
Input,
|
||||
Output,
|
||||
Error = IApplicationError
|
||||
> extends IApplicationService {
|
||||
execute(input: Input): Promise<Result<Output, Error>>;
|
||||
}
|
||||
5
core/shared/application/UseCase.ts
Normal file
5
core/shared/application/UseCase.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import type { Presenter } from '../presentation';
|
||||
|
||||
export interface UseCase<Input, OutputDTO, ViewModel, P extends Presenter<OutputDTO, ViewModel>> {
|
||||
execute(input: Input, presenter: P): Promise<void> | void;
|
||||
}
|
||||
3
core/shared/application/index.ts
Normal file
3
core/shared/application/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './UseCase';
|
||||
export * from './AsyncUseCase';
|
||||
export * from './Service';
|
||||
3
core/shared/domain/Entity.ts
Normal file
3
core/shared/domain/Entity.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface IEntity<Id = string> {
|
||||
readonly id: Id;
|
||||
}
|
||||
10
core/shared/domain/IDomainEvent.ts
Normal file
10
core/shared/domain/IDomainEvent.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export interface IDomainEvent<T = any> {
|
||||
readonly eventType: string;
|
||||
readonly aggregateId: string;
|
||||
readonly eventData: T;
|
||||
readonly occurredAt: Date;
|
||||
}
|
||||
|
||||
export interface IDomainEventPublisher {
|
||||
publish(event: IDomainEvent): Promise<void>;
|
||||
}
|
||||
7
core/shared/domain/Option.ts
Normal file
7
core/shared/domain/Option.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export function coalesce<T>(value: T | undefined | null, fallback: T): T {
|
||||
return value ?? fallback;
|
||||
}
|
||||
|
||||
export function present<T>(value: T | undefined | null): T | undefined {
|
||||
return value === undefined || value === null ? undefined : value;
|
||||
}
|
||||
24
core/shared/domain/Service.ts
Normal file
24
core/shared/domain/Service.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { Result } from '../result/Result';
|
||||
import type { IDomainError } from '../errors/DomainError';
|
||||
|
||||
export interface IDomainService {
|
||||
readonly serviceName?: string;
|
||||
}
|
||||
|
||||
export interface IDomainCalculationService<Input, Output> extends IDomainService {
|
||||
calculate(input: Input): Output;
|
||||
}
|
||||
|
||||
export interface IResultDomainCalculationService<Input, Output, Error = IDomainError>
|
||||
extends IDomainService {
|
||||
calculate(input: Input): Result<Output, Error>;
|
||||
}
|
||||
|
||||
export interface IDomainValidationService<Input, Output, Error = IDomainError>
|
||||
extends IDomainService {
|
||||
validate(input: Input): Result<Output, Error>;
|
||||
}
|
||||
|
||||
export interface IDomainFactoryService<Input, Output> extends IDomainService {
|
||||
create(input: Input): Output;
|
||||
}
|
||||
4
core/shared/domain/ValueObject.ts
Normal file
4
core/shared/domain/ValueObject.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface IValueObject<Props> {
|
||||
readonly props: Props;
|
||||
equals(other: IValueObject<Props>): boolean;
|
||||
}
|
||||
4
core/shared/domain/index.ts
Normal file
4
core/shared/domain/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './Entity';
|
||||
export * from './ValueObject';
|
||||
export * from './Service';
|
||||
export * from './Option';
|
||||
14
core/shared/errors/ApplicationError.ts
Normal file
14
core/shared/errors/ApplicationError.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export type CommonApplicationErrorKind =
|
||||
| 'not_found'
|
||||
| 'forbidden'
|
||||
| 'conflict'
|
||||
| 'validation'
|
||||
| 'unknown'
|
||||
| string;
|
||||
|
||||
export interface IApplicationError<K extends string = CommonApplicationErrorKind, D = unknown> extends Error {
|
||||
readonly type: 'application';
|
||||
readonly context: string;
|
||||
readonly kind: K;
|
||||
readonly details?: D;
|
||||
}
|
||||
7
core/shared/errors/DomainError.ts
Normal file
7
core/shared/errors/DomainError.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export type CommonDomainErrorKind = 'validation' | 'invariant' | string;
|
||||
|
||||
export interface IDomainError<K extends string = CommonDomainErrorKind> extends Error {
|
||||
readonly type: 'domain';
|
||||
readonly context: string;
|
||||
readonly kind: K;
|
||||
}
|
||||
2
core/shared/errors/index.ts
Normal file
2
core/shared/errors/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './DomainError';
|
||||
export * from './ApplicationError';
|
||||
6
core/shared/index.ts
Normal file
6
core/shared/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export * from './result/Result';
|
||||
export * as application from './application';
|
||||
export * as domain from './domain';
|
||||
export * as errors from './errors';
|
||||
export * from './presentation';
|
||||
export * from './application/AsyncUseCase';
|
||||
6
core/shared/logger/ILogger.ts
Normal file
6
core/shared/logger/ILogger.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface ILogger {
|
||||
debug(message: string, context?: Record<string, any>): void;
|
||||
info(message: string, context?: Record<string, any>): void;
|
||||
warn(message: string, context?: Record<string, any>): void;
|
||||
error(message: string, error?: Error, context?: Record<string, any>): void;
|
||||
}
|
||||
20
core/shared/logging/ConsoleLogger.ts
Normal file
20
core/shared/logging/ConsoleLogger.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { ILogger } from './ILogger';
|
||||
|
||||
export class ConsoleLogger implements ILogger {
|
||||
debug(message: string, ...args: any[]): void {
|
||||
console.debug(message, ...args);
|
||||
}
|
||||
|
||||
info(message: string, ...args: any[]): void {
|
||||
console.info(message, ...args);
|
||||
}
|
||||
|
||||
warn(message: string, ...args: any[]): void {
|
||||
console.warn(message, ...args);
|
||||
}
|
||||
|
||||
error(message: string, ...args: any[]): void {
|
||||
console.error(message, ...args);
|
||||
}
|
||||
|
||||
}
|
||||
7
core/shared/logging/ILogger.ts
Normal file
7
core/shared/logging/ILogger.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
export interface ILogger {
|
||||
debug(message: string, ...args: any[]): void;
|
||||
info(message: string, ...args: any[]): void;
|
||||
warn(message: string, ...args: any[]): void;
|
||||
error(message: string, ...args: any[]): void;
|
||||
}
|
||||
5
core/shared/presentation/Presenter.ts
Normal file
5
core/shared/presentation/Presenter.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export interface Presenter<InputDTO, ViewModel> {
|
||||
present(input: InputDTO): void;
|
||||
getViewModel(): ViewModel | null;
|
||||
reset(): void;
|
||||
}
|
||||
1
core/shared/presentation/index.ts
Normal file
1
core/shared/presentation/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './Presenter';
|
||||
78
core/shared/result/Result.ts
Normal file
78
core/shared/result/Result.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
export class Result<T, E = Error> {
|
||||
private constructor(
|
||||
private readonly _value?: T,
|
||||
private readonly _error?: E,
|
||||
private readonly _isSuccess: boolean = true
|
||||
) {}
|
||||
|
||||
static ok<T, E = Error>(value: T): Result<T, E> {
|
||||
return new Result<T, E>(value, undefined, true);
|
||||
}
|
||||
|
||||
static err<T, E = Error>(error: E): Result<T, E> {
|
||||
return new Result<T, E>(undefined, error, false);
|
||||
}
|
||||
|
||||
isOk(): boolean {
|
||||
return this._isSuccess;
|
||||
}
|
||||
|
||||
isErr(): boolean {
|
||||
return !this._isSuccess;
|
||||
}
|
||||
|
||||
unwrap(): T {
|
||||
if (!this._isSuccess) {
|
||||
throw new Error('Called unwrap on an error result');
|
||||
}
|
||||
return this._value!;
|
||||
}
|
||||
|
||||
unwrapOr(defaultValue: T): T {
|
||||
return this._isSuccess ? this._value! : defaultValue;
|
||||
}
|
||||
|
||||
unwrapErr(): E {
|
||||
if (this._isSuccess) {
|
||||
throw new Error('Called unwrapErr on a success result');
|
||||
}
|
||||
return this._error!;
|
||||
}
|
||||
|
||||
map<U>(fn: (value: T) => U): Result<U, E> {
|
||||
if (this._isSuccess) {
|
||||
return Result.ok(fn(this._value!));
|
||||
}
|
||||
return Result.err(this._error!);
|
||||
}
|
||||
|
||||
mapErr<F>(fn: (error: E) => F): Result<T, F> {
|
||||
if (!this._isSuccess) {
|
||||
return Result.err(fn(this._error!));
|
||||
}
|
||||
return Result.ok(this._value!);
|
||||
}
|
||||
|
||||
andThen<U>(fn: (value: T) => Result<U, E>): Result<U, E> {
|
||||
if (this._isSuccess) {
|
||||
return fn(this._value!);
|
||||
}
|
||||
return Result.err(this._error!);
|
||||
}
|
||||
|
||||
/**
|
||||
* Direct access to the value (for testing convenience).
|
||||
* Prefer using unwrap() in production code.
|
||||
*/
|
||||
get value(): T | undefined {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Direct access to the error (for testing convenience).
|
||||
* Prefer using unwrapErr() in production code.
|
||||
*/
|
||||
get error(): E | undefined {
|
||||
return this._error;
|
||||
}
|
||||
}
|
||||
69
core/shared/tests/unit/logging/ConsoleLogger.test.ts
Normal file
69
core/shared/tests/unit/logging/ConsoleLogger.test.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { vi } from 'vitest';
|
||||
import { ConsoleLogger } from '../../../logging/ConsoleLogger'; // Assuming ConsoleLogger is here
|
||||
|
||||
describe('ConsoleLogger', () => {
|
||||
let logger: ConsoleLogger;
|
||||
let consoleDebugSpy: vi.SpyInstance;
|
||||
let consoleInfoSpy: vi.SpyInstance;
|
||||
let consoleWarnSpy: vi.SpyInstance;
|
||||
let consoleErrorSpy: vi.SpyInstance;
|
||||
let consoleLogSpy: vi.SpyInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
logger = new ConsoleLogger();
|
||||
consoleDebugSpy = vi.spyOn(console, 'debug').mockImplementation(() => {});
|
||||
consoleInfoSpy = vi.spyOn(console, 'info').mockImplementation(() => {});
|
||||
consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
consoleDebugSpy.mockRestore();
|
||||
consoleInfoSpy.mockRestore();
|
||||
consoleWarnSpy.mockRestore();
|
||||
consoleErrorSpy.mockRestore();
|
||||
consoleLogSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should call console.debug with the correct arguments when debug is called', () => {
|
||||
const message = 'Debug message';
|
||||
const context = { key: 'value' };
|
||||
logger.debug(message, context);
|
||||
expect(consoleDebugSpy).toHaveBeenCalledTimes(1);
|
||||
expect(consoleDebugSpy).toHaveBeenCalledWith(message, context);
|
||||
});
|
||||
|
||||
it('should call console.info with the correct arguments when info is called', () => {
|
||||
const message = 'Info message';
|
||||
const context = { key: 'value' };
|
||||
logger.info(message, context);
|
||||
expect(consoleInfoSpy).toHaveBeenCalledTimes(1);
|
||||
expect(consoleInfoSpy).toHaveBeenCalledWith(message, context);
|
||||
});
|
||||
|
||||
it('should call console.warn with the correct arguments when warn is called', () => {
|
||||
const message = 'Warn message';
|
||||
const context = { key: 'value' };
|
||||
logger.warn(message, context);
|
||||
expect(consoleWarnSpy).toHaveBeenCalledTimes(1);
|
||||
expect(consoleWarnSpy).toHaveBeenCalledWith(message, context);
|
||||
});
|
||||
|
||||
it('should call console.error with the correct arguments when error is called', () => {
|
||||
const message = 'Error message';
|
||||
const error = new Error('Something went wrong');
|
||||
const context = { key: 'value' };
|
||||
logger.error(message, error, context);
|
||||
expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(message, error, context);
|
||||
});
|
||||
|
||||
it('should call console.log with the correct arguments when log is called', () => {
|
||||
const message = 'Log message';
|
||||
const context = { key: 'value' };
|
||||
logger.log(message, context);
|
||||
expect(consoleLogSpy).toHaveBeenCalledTimes(1);
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith(message, context);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user