From eed3e35274819c2b50741c6eb9a53584a278acbf Mon Sep 17 00:00:00 2001 From: marcus-sa <8391194+marcus-sa@users.noreply.github.com> Date: Sat, 24 Aug 2024 12:22:36 +0200 Subject: [PATCH] refactor services --- .eslintrc.json | 5 +- accounting-service-api/README.md | 11 --- accounting-service-api/src/lib/entities.ts | 26 ------- accounting-service-api/src/lib/replies.ts | 26 ------- accounting-service-api/src/lib/services.ts | 29 -------- accounting-service/.env | 6 -- accounting-service/.env.prod | 3 - accounting-service/src/accounting.service.ts | 54 -------------- accounting-service/src/config.ts | 3 - accounting-service/src/main.ts | 19 ----- .../src/accounting/accounting.module.ts | 4 +- api-gateway/src/consumer/consumer.module.ts | 4 +- common/src/lib/repository.ts | 30 +++----- consumer-service-api/README.md | 11 --- consumer-service-api/src/lib/entities.ts | 10 --- consumer-service-api/src/lib/events.ts | 5 -- consumer-service/.env | 5 -- consumer-service/src/config.ts | 3 - consumer-service/src/consumer.repository.ts | 4 - .../.eslintrc.json | 0 customer-service-api/README.md | 11 +++ .../package.json | 2 +- .../project.json | 14 ++-- .../src/index.ts | 0 .../src/lib/dtos.ts | 0 customer-service-api/src/lib/entities.ts | 28 +++++++ customer-service-api/src/lib/events.ts | 5 ++ .../src/lib/replies.ts | 6 +- .../src/lib/services.ts | 10 +-- .../tsconfig.json | 0 .../tsconfig.lib.json | 0 .../tsconfig.spec.json | 0 .../vite.config.ts | 4 +- .../.eslintrc.json | 0 .../project.json | 26 +++---- customer-service/src/config.ts | 3 + customer-service/src/customer.repository.ts | 4 + .../src/customer.service.ts | 28 +++---- .../src/main.ts | 16 ++-- .../tsconfig.app.json | 0 .../tsconfig.json | 0 .../tsconfig.spec.json | 0 .../vite.config.ts | 0 kitchen-service-api/src/lib/replies.ts | 10 ++- kitchen-service-api/src/lib/services.ts | 5 +- kitchen-service/src/kitchen.service.ts | 26 +++---- order-service-api/src/lib/entities.ts | 6 +- order-service-api/src/lib/sagas.ts | 4 +- order-service/src/sagas/cancel-order.saga.ts | 4 +- order-service/src/sagas/create-order.saga.ts | 74 ++++++++++++++----- .../.eslintrc.json | 0 payment-service-api/README.md | 11 +++ .../package.json | 2 +- .../project.json | 14 ++-- .../src/index.ts | 0 payment-service-api/src/lib/entities.ts | 6 ++ payment-service-api/src/lib/replies.ts | 22 ++++++ payment-service-api/src/lib/services.ts | 28 +++++++ .../tsconfig.json | 0 .../tsconfig.lib.json | 0 .../tsconfig.spec.json | 0 .../vite.config.ts | 4 +- .../.eslintrc.json | 0 .../Dockerfile | 2 +- .../project.json | 28 +++---- payment-service/src/config.ts | 3 + payment-service/src/main.ts | 19 +++++ .../src/payment.repository.ts | 10 +-- payment-service/src/payment.service.ts | 30 ++++++++ .../tsconfig.app.json | 0 .../tsconfig.json | 0 .../tsconfig.spec.json | 0 .../vite.config.ts | 0 tsconfig.base.json | 4 +- 74 files changed, 360 insertions(+), 367 deletions(-) delete mode 100644 accounting-service-api/README.md delete mode 100644 accounting-service-api/src/lib/entities.ts delete mode 100644 accounting-service-api/src/lib/replies.ts delete mode 100644 accounting-service-api/src/lib/services.ts delete mode 100644 accounting-service/.env delete mode 100644 accounting-service/.env.prod delete mode 100644 accounting-service/src/accounting.service.ts delete mode 100644 accounting-service/src/config.ts delete mode 100644 accounting-service/src/main.ts delete mode 100644 consumer-service-api/README.md delete mode 100644 consumer-service-api/src/lib/entities.ts delete mode 100644 consumer-service-api/src/lib/events.ts delete mode 100644 consumer-service/.env delete mode 100644 consumer-service/src/config.ts delete mode 100644 consumer-service/src/consumer.repository.ts rename {accounting-service-api => customer-service-api}/.eslintrc.json (100%) create mode 100644 customer-service-api/README.md rename {consumer-service-api => customer-service-api}/package.json (73%) rename {consumer-service-api => customer-service-api}/project.json (51%) rename {consumer-service-api => customer-service-api}/src/index.ts (100%) rename {consumer-service-api => customer-service-api}/src/lib/dtos.ts (100%) create mode 100644 customer-service-api/src/lib/entities.ts create mode 100644 customer-service-api/src/lib/events.ts rename {consumer-service-api => customer-service-api}/src/lib/replies.ts (57%) rename {consumer-service-api => customer-service-api}/src/lib/services.ts (57%) rename {accounting-service-api => customer-service-api}/tsconfig.json (100%) rename {accounting-service-api => customer-service-api}/tsconfig.lib.json (100%) rename {accounting-service-api => customer-service-api}/tsconfig.spec.json (100%) rename {consumer-service-api => customer-service-api}/vite.config.ts (81%) rename {accounting-service => customer-service}/.eslintrc.json (100%) rename {consumer-service => customer-service}/project.json (74%) create mode 100644 customer-service/src/config.ts create mode 100644 customer-service/src/customer.repository.ts rename consumer-service/src/consumer.service.ts => customer-service/src/customer.service.ts (54%) rename {consumer-service => customer-service}/src/main.ts (57%) rename {accounting-service => customer-service}/tsconfig.app.json (100%) rename {accounting-service => customer-service}/tsconfig.json (100%) rename {accounting-service => customer-service}/tsconfig.spec.json (100%) rename {accounting-service => customer-service}/vite.config.ts (100%) rename {consumer-service-api => payment-service-api}/.eslintrc.json (100%) create mode 100644 payment-service-api/README.md rename {accounting-service-api => payment-service-api}/package.json (72%) rename {accounting-service-api => payment-service-api}/project.json (50%) rename {accounting-service-api => payment-service-api}/src/index.ts (100%) create mode 100644 payment-service-api/src/lib/entities.ts create mode 100644 payment-service-api/src/lib/replies.ts create mode 100644 payment-service-api/src/lib/services.ts rename {consumer-service-api => payment-service-api}/tsconfig.json (100%) rename {consumer-service-api => payment-service-api}/tsconfig.lib.json (100%) rename {consumer-service-api => payment-service-api}/tsconfig.spec.json (100%) rename {accounting-service-api => payment-service-api}/vite.config.ts (81%) rename {consumer-service => payment-service}/.eslintrc.json (100%) rename {accounting-service => payment-service}/Dockerfile (80%) rename {accounting-service => payment-service}/project.json (71%) create mode 100644 payment-service/src/config.ts create mode 100644 payment-service/src/main.ts rename accounting-service/src/account.repository.ts => payment-service/src/payment.repository.ts (57%) create mode 100644 payment-service/src/payment.service.ts rename {consumer-service => payment-service}/tsconfig.app.json (100%) rename {consumer-service => payment-service}/tsconfig.json (100%) rename {consumer-service => payment-service}/tsconfig.spec.json (100%) rename {consumer-service => payment-service}/vite.config.ts (100%) diff --git a/.eslintrc.json b/.eslintrc.json index 8bc6199..63ecd3c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -25,7 +25,10 @@ { "files": ["*.ts", "*.tsx"], "extends": ["plugin:@nx/typescript"], - "rules": {} + "rules": { + "@typescript-eslint/no-empty-interface": ["warn"], + "@typescript-eslint/no-empty-function": ["warn"] + } }, { "files": ["*.js", "*.jsx"], diff --git a/accounting-service-api/README.md b/accounting-service-api/README.md deleted file mode 100644 index 1f26b18..0000000 --- a/accounting-service-api/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# accounting-service-api - -This library was generated with [Nx](https://nx.dev). - -## Building - -Run `nx build accounting-service-api` to build the library. - -## Running unit tests - -Run `nx test accounting-service-api` to execute the unit tests via [Vitest](https://vitest.dev/). diff --git a/accounting-service-api/src/lib/entities.ts b/accounting-service-api/src/lib/entities.ts deleted file mode 100644 index 6aabe7a..0000000 --- a/accounting-service-api/src/lib/entities.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { entity, Unique, uuid, UUID } from '@deepkit/type'; -import { Writable } from 'type-fest'; - -import { AccountDisabled } from './replies'; - -@entity.name('account') -export class Account { - readonly id: UUID = uuid(); - readonly disabled: boolean = false; - - constructor(readonly consumerId: UUID & Unique) {} - - assertEnabled(): void { - if (!this.disabled) { - throw new AccountDisabled(this.id); - } - } - - disable(this: Writable) { - this.disabled = true; - } - - enabled(this: Writable) { - this.disabled = false; - } -} diff --git a/accounting-service-api/src/lib/replies.ts b/accounting-service-api/src/lib/replies.ts deleted file mode 100644 index e7b7642..0000000 --- a/accounting-service-api/src/lib/replies.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { UUID } from '@deepkit/type'; - -export class AccountDisabled extends Error { - constructor(readonly accountId: UUID) { - super(); - } -} - -export class AccountCreated { - constructor(readonly accountId: UUID) {} -} - -export class AccountNotFound extends Error {} - -export class AccountAuthorized { - constructor(readonly accountId: UUID) {} -} - -export class AccountAuthorizationFailed extends Error { - constructor( - readonly accountId: UUID, - readonly reason: string, - ) { - super(); - } -} diff --git a/accounting-service-api/src/lib/services.ts b/accounting-service-api/src/lib/services.ts deleted file mode 100644 index cc967e6..0000000 --- a/accounting-service-api/src/lib/services.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { RestateService } from 'deepkit-restate'; -import { UUID } from '@deepkit/type'; - -import { ConsumerCreatedEvent } from '@ftgo/consumer-service-api'; -import { Money } from '@ftgo/common'; - -import { AccountAuthorized, AccountDisabled } from './replies'; - -export interface AccountingServiceHandlers { - createAccount(event: ConsumerCreatedEvent): Promise; - authorize( - consumerId: UUID, - orderId: UUID, - orderTotal: Money, - ): Promise; - reverseAuthorization( - consumerId: UUID, - orderId: UUID, - orderTotal: Money, - ): Promise; - // disable(id: UUID): Promise; - // enable(id: UUID): Promise; -} - -export type AccountingServiceApi = RestateService< - 'Accounting', - AccountingServiceHandlers, - [AccountDisabled] ->; diff --git a/accounting-service/.env b/accounting-service/.env deleted file mode 100644 index c7df185..0000000 --- a/accounting-service/.env +++ /dev/null @@ -1,6 +0,0 @@ -# Restate -RESTATE_SERVER_PORT=9081 - -# Database -DATABASE_SCHEMA=accounting - diff --git a/accounting-service/.env.prod b/accounting-service/.env.prod deleted file mode 100644 index 70e4111..0000000 --- a/accounting-service/.env.prod +++ /dev/null @@ -1,3 +0,0 @@ -# Restate -RESTATE_SERVER_HOST=ftgo-accounting-service.internal -RESTATE_SERVER_PORT=9080 diff --git a/accounting-service/src/accounting.service.ts b/accounting-service/src/accounting.service.ts deleted file mode 100644 index 4ed83cc..0000000 --- a/accounting-service/src/accounting.service.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { restate } from 'deepkit-restate'; -import { UUID } from '@deepkit/type'; - -import { ConsumerCreatedEvent } from '@ftgo/consumer-service-api'; -import { Money } from '@ftgo/common'; -import { - AccountAuthorized, - AccountingServiceApi, - AccountingServiceHandlers, -} from '@ftgo/accounting-service-api'; - -import { AccountRepository } from './account.repository'; - -@restate.service() -export class AccountingService implements AccountingServiceHandlers { - constructor(private readonly account: AccountRepository) {} - - // @ts-ignore - @(restate.event().handler()) - async createAccount({ consumer }: ConsumerCreatedEvent): Promise { - await this.account.create(consumer.id); - } - - @restate.handler() - async authorize( - consumerId: UUID, - orderId: UUID, - orderTotal: Money, - ): Promise { - const account = await this.account.findByConsumer(consumerId); - account.assertEnabled(); - return new AccountAuthorized(account.id); - } - - @restate.handler() - async reverseAuthorization( - consumerId: UUID, - orderId: UUID, - orderTotal: Money, - ): Promise { - const account = await this.account.findByConsumer(consumerId); - account.assertEnabled(); - } - - // @restate.handler() - // async disable(id: UUID): Promise { - // return Promise.resolve(undefined); - // } - // - // @restate.handler() - // async enable(id: UUID): Promise { - // return Promise.resolve(undefined); - // } -} diff --git a/accounting-service/src/config.ts b/accounting-service/src/config.ts deleted file mode 100644 index 6b83e08..0000000 --- a/accounting-service/src/config.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { ServiceConfig } from '@ftgo/common'; - -export class AccountingServiceConfig extends ServiceConfig {} diff --git a/accounting-service/src/main.ts b/accounting-service/src/main.ts deleted file mode 100644 index 74e0614..0000000 --- a/accounting-service/src/main.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { App } from '@deepkit/app'; -import { FrameworkModule } from '@deepkit/framework'; -import { RestateModule } from 'deepkit-restate'; - -import { provideDatabase } from '@ftgo/common'; -import { Account } from '@ftgo/accounting-service-api'; - -import { AccountingServiceConfig } from './config'; -import { AccountingService } from './accounting.service'; -import { AccountRepository } from './account.repository'; - -void new App({ - config: AccountingServiceConfig, - imports: [new FrameworkModule(), new RestateModule()], - controllers: [AccountingService], - providers: [provideDatabase([Account]), AccountRepository], -}) - .loadConfigFromEnv({ prefix: '' }) - .run(); diff --git a/api-gateway/src/accounting/accounting.module.ts b/api-gateway/src/accounting/accounting.module.ts index 0eaf2e9..ea36a16 100644 --- a/api-gateway/src/accounting/accounting.module.ts +++ b/api-gateway/src/accounting/accounting.module.ts @@ -1,11 +1,11 @@ import { createModule } from '@deepkit/app'; import { provideRestateServiceProxy } from 'deepkit-restate'; -import { AccountingServiceApi } from '@ftgo/accounting-service-api'; +import { PaymentServiceApi } from '@ftgo/payment-service-api'; import { AccountingController } from './accounting.controller'; export class AccountingModule extends createModule({ controllers: [AccountingController], - providers: [provideRestateServiceProxy()], + providers: [provideRestateServiceProxy()], }) {} diff --git a/api-gateway/src/consumer/consumer.module.ts b/api-gateway/src/consumer/consumer.module.ts index 2fbb02e..ce38da6 100644 --- a/api-gateway/src/consumer/consumer.module.ts +++ b/api-gateway/src/consumer/consumer.module.ts @@ -1,11 +1,11 @@ import { createModule } from '@deepkit/app'; import { provideRestateServiceProxy } from 'deepkit-restate'; -import { ConsumerServiceApi } from '@ftgo/consumer-service-api'; +import { CustomerServiceApi } from '@ftgo/customer-service-api'; import { ConsumerController } from './consumer.controller'; export class ConsumerModule extends createModule({ controllers: [ConsumerController], - providers: [provideRestateServiceProxy()], + providers: [provideRestateServiceProxy()], }) {} diff --git a/common/src/lib/repository.ts b/common/src/lib/repository.ts index b784e79..cdec940 100644 --- a/common/src/lib/repository.ts +++ b/common/src/lib/repository.ts @@ -6,14 +6,9 @@ import { TypeClass, } from '@deepkit/type'; import { RestateContextStorage, RestateCustomContext } from 'deepkit-restate'; -import { - DatabaseQueryModel, - DeleteResult, - OrmEntity, - PatchResult, -} from '@deepkit/orm'; +import { DatabaseQueryModel, OrmEntity } from '@deepkit/orm'; -import { Database } from '@ftgo/common'; +import { Database } from './database'; export class RestateRepository { readonly #type: TypeClass; @@ -30,11 +25,8 @@ export class RestateRepository { return this.contextStorage.getStore()!; } - async delete( - filter: DatabaseQueryModel['filter'], - ): Promise> { - // TODO: serde - return await this.#ctx.run>(() => + async delete(filter: DatabaseQueryModel['filter']): Promise { + await this.#ctx.run(() => this.database.query(this.#type.classType).filter(filter).deleteOne(), ); } @@ -53,18 +45,18 @@ export class RestateRepository { async patch( filter: DatabaseQueryModel['filter'], changes: ChangesInterface | DeepPartial, - ): Promise> { - // TODO: serde - return await this.#ctx.run>(() => - this.database + ): Promise { + return await this.#ctx.run(async () => { + const { returning } = await this.database .query(this.#type.classType) .filter(filter) - .patchOne(changes), - ); + .patchOne(changes); + return returning as E; + }, this.#type); } async create(...args: ConstructorParameters>): Promise { - return await this.#ctx.run(async () => { + return await this.#ctx.run(async () => { const et = new this.#type.classType(...args); await this.database.persist(et); return et; diff --git a/consumer-service-api/README.md b/consumer-service-api/README.md deleted file mode 100644 index abf48e9..0000000 --- a/consumer-service-api/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# consumer-service-api - -This library was generated with [Nx](https://nx.dev). - -## Building - -Run `nx build consumer-service-api` to build the library. - -## Running unit tests - -Run `nx test consumer-service-api` to execute the unit tests via [Vitest](https://vitest.dev/). diff --git a/consumer-service-api/src/lib/entities.ts b/consumer-service-api/src/lib/entities.ts deleted file mode 100644 index 5c0e428..0000000 --- a/consumer-service-api/src/lib/entities.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { entity, PrimaryKey, uuid, UUID } from '@deepkit/type'; - -import { PersonName } from '@ftgo/common'; - -@entity.name('consumer') -export class Consumer { - readonly id: UUID & PrimaryKey = uuid(); - - constructor(readonly name: PersonName) {} -} diff --git a/consumer-service-api/src/lib/events.ts b/consumer-service-api/src/lib/events.ts deleted file mode 100644 index 99e0d9e..0000000 --- a/consumer-service-api/src/lib/events.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Consumer } from './entities'; - -export class ConsumerCreatedEvent { - constructor(readonly consumer: Consumer) {} -} diff --git a/consumer-service/.env b/consumer-service/.env deleted file mode 100644 index 6148f25..0000000 --- a/consumer-service/.env +++ /dev/null @@ -1,5 +0,0 @@ -# Restate -RESTATE_SERVER_PORT=9082 - -# Database -DATABASE_SCHEMA=consumer diff --git a/consumer-service/src/config.ts b/consumer-service/src/config.ts deleted file mode 100644 index e2e10c5..0000000 --- a/consumer-service/src/config.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { ServiceConfig } from '@ftgo/common'; - -export class ConsumerServiceConfig extends ServiceConfig {} diff --git a/consumer-service/src/consumer.repository.ts b/consumer-service/src/consumer.repository.ts deleted file mode 100644 index 7f8ca1f..0000000 --- a/consumer-service/src/consumer.repository.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { RestateRepository } from '@ftgo/common'; -import { Consumer } from '@ftgo/consumer-service-api'; - -export class ConsumerRepository extends RestateRepository {} diff --git a/accounting-service-api/.eslintrc.json b/customer-service-api/.eslintrc.json similarity index 100% rename from accounting-service-api/.eslintrc.json rename to customer-service-api/.eslintrc.json diff --git a/customer-service-api/README.md b/customer-service-api/README.md new file mode 100644 index 0000000..9beac73 --- /dev/null +++ b/customer-service-api/README.md @@ -0,0 +1,11 @@ +# customer-service-api + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build customer-service-api` to build the library. + +## Running unit tests + +Run `nx test customer-service-api` to execute the unit tests via [Vitest](https://vitest.dev/). diff --git a/consumer-service-api/package.json b/customer-service-api/package.json similarity index 73% rename from consumer-service-api/package.json rename to customer-service-api/package.json index 17d4e86..53db3fc 100644 --- a/consumer-service-api/package.json +++ b/customer-service-api/package.json @@ -1,5 +1,5 @@ { - "name": "@ftgo/consumer-service-api", + "name": "@ftgo/customer-service-api", "version": "0.0.1", "type": "module", "dependencies": { diff --git a/consumer-service-api/project.json b/customer-service-api/project.json similarity index 51% rename from consumer-service-api/project.json rename to customer-service-api/project.json index fabb318..f9effa5 100644 --- a/consumer-service-api/project.json +++ b/customer-service-api/project.json @@ -1,7 +1,7 @@ { - "name": "consumer-service-api", + "name": "customer-service-api", "$schema": "../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "consumer-service-api/src", + "sourceRoot": "customer-service-api/src", "projectType": "library", "tags": [], "targets": { @@ -9,17 +9,17 @@ "executor": "@nx/js:tsc", "outputs": ["{options.outputPath}"], "options": { - "outputPath": "dist/consumer-service-api", - "main": "consumer-service-api/src/index.ts", - "tsConfig": "consumer-service-api/tsconfig.lib.json", - "assets": ["consumer-service-api/*.md"] + "outputPath": "dist/customer-service-api", + "main": "customer-service-api/src/index.ts", + "tsConfig": "customer-service-api/tsconfig.lib.json", + "assets": ["customer-service-api/*.md"] } }, "test": { "executor": "@nx/vite:test", "outputs": ["{options.reportsDirectory}"], "options": { - "reportsDirectory": "../coverage/consumer-service-api" + "reportsDirectory": "../coverage/customer-service-api" } } } diff --git a/consumer-service-api/src/index.ts b/customer-service-api/src/index.ts similarity index 100% rename from consumer-service-api/src/index.ts rename to customer-service-api/src/index.ts diff --git a/consumer-service-api/src/lib/dtos.ts b/customer-service-api/src/lib/dtos.ts similarity index 100% rename from consumer-service-api/src/lib/dtos.ts rename to customer-service-api/src/lib/dtos.ts diff --git a/customer-service-api/src/lib/entities.ts b/customer-service-api/src/lib/entities.ts new file mode 100644 index 0000000..7eb050f --- /dev/null +++ b/customer-service-api/src/lib/entities.ts @@ -0,0 +1,28 @@ +import { entity, PrimaryKey, uuid, UUID } from '@deepkit/type'; +import { Writable } from 'type-fest'; + +import { PersonName } from '@ftgo/common'; + +import { CustomerDisabled } from './replies'; + +@entity.name('customer') +export class Customer { + readonly id: UUID & PrimaryKey = uuid(); + readonly disabled: boolean = false; + + constructor(readonly name: PersonName) {} + + assertEnabled(): void { + if (!this.disabled) { + throw new CustomerDisabled(this.id); + } + } + + disable(this: Writable) { + this.disabled = true; + } + + enabled(this: Writable) { + this.disabled = false; + } +} diff --git a/customer-service-api/src/lib/events.ts b/customer-service-api/src/lib/events.ts new file mode 100644 index 0000000..45c1536 --- /dev/null +++ b/customer-service-api/src/lib/events.ts @@ -0,0 +1,5 @@ +import { Customer } from './entities'; + +export class CustomerCreatedEvent { + constructor(readonly customer: Customer) {} +} diff --git a/consumer-service-api/src/lib/replies.ts b/customer-service-api/src/lib/replies.ts similarity index 57% rename from consumer-service-api/src/lib/replies.ts rename to customer-service-api/src/lib/replies.ts index 16a45a6..12914ee 100644 --- a/consumer-service-api/src/lib/replies.ts +++ b/customer-service-api/src/lib/replies.ts @@ -1,18 +1,18 @@ import { UUID } from '@deepkit/type'; -export class ConsumerNotFound extends Error { +export class CustomerNotFound extends Error { constructor(readonly id: UUID) { super(); } } -export class ConsumerCreated extends Error { +export class CustomerVerificationFailed extends Error { constructor(readonly id: UUID) { super(); } } -export class ConsumerVerificationFailed extends Error { +export class CustomerDisabled extends Error { constructor(readonly id: UUID) { super(); } diff --git a/consumer-service-api/src/lib/services.ts b/customer-service-api/src/lib/services.ts similarity index 57% rename from consumer-service-api/src/lib/services.ts rename to customer-service-api/src/lib/services.ts index 0b3e017..e93cf8e 100644 --- a/consumer-service-api/src/lib/services.ts +++ b/customer-service-api/src/lib/services.ts @@ -3,9 +3,9 @@ import { UUID } from '@deepkit/type'; import { Money, PersonName } from '@ftgo/common'; -import { ConsumerNotFound, ConsumerVerificationFailed } from './replies'; +import { CustomerNotFound, CustomerVerificationFailed } from './replies'; -export interface ConsumerServiceHandlers { +export interface CustomerServiceHandlers { create(name: PersonName): Promise; validateOrder( consumerId: UUID, @@ -14,8 +14,8 @@ export interface ConsumerServiceHandlers { ): Promise; } -export type ConsumerServiceApi = RestateService< +export type CustomerServiceApi = RestateService< 'Consumer', - ConsumerServiceHandlers, - [ConsumerNotFound, ConsumerVerificationFailed] + CustomerServiceHandlers, + [CustomerNotFound, CustomerVerificationFailed] >; diff --git a/accounting-service-api/tsconfig.json b/customer-service-api/tsconfig.json similarity index 100% rename from accounting-service-api/tsconfig.json rename to customer-service-api/tsconfig.json diff --git a/accounting-service-api/tsconfig.lib.json b/customer-service-api/tsconfig.lib.json similarity index 100% rename from accounting-service-api/tsconfig.lib.json rename to customer-service-api/tsconfig.lib.json diff --git a/accounting-service-api/tsconfig.spec.json b/customer-service-api/tsconfig.spec.json similarity index 100% rename from accounting-service-api/tsconfig.spec.json rename to customer-service-api/tsconfig.spec.json diff --git a/consumer-service-api/vite.config.ts b/customer-service-api/vite.config.ts similarity index 81% rename from consumer-service-api/vite.config.ts rename to customer-service-api/vite.config.ts index 8d2b25e..1dc9377 100644 --- a/consumer-service-api/vite.config.ts +++ b/customer-service-api/vite.config.ts @@ -4,7 +4,7 @@ import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; export default defineConfig({ root: __dirname, - cacheDir: '../node_modules/.vite/consumer-service-api', + cacheDir: '../node_modules/.vite/customer-service-api', plugins: [nxViteTsPaths()], @@ -20,7 +20,7 @@ export default defineConfig({ include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], reporters: ['default'], coverage: { - reportsDirectory: '../coverage/consumer-service-api', + reportsDirectory: '../coverage/customer-service-api', provider: 'v8', }, }, diff --git a/accounting-service/.eslintrc.json b/customer-service/.eslintrc.json similarity index 100% rename from accounting-service/.eslintrc.json rename to customer-service/.eslintrc.json diff --git a/consumer-service/project.json b/customer-service/project.json similarity index 74% rename from consumer-service/project.json rename to customer-service/project.json index 5581361..c384906 100644 --- a/consumer-service/project.json +++ b/customer-service/project.json @@ -1,9 +1,9 @@ { - "name": "consumer-service", + "name": "customer-service", "$schema": "../node_modules/nx/schemas/project-schema.json", "projectType": "application", "prefix": "ftgo", - "sourceRoot": "consumer-service/src", + "sourceRoot": "customer-service/src", "tags": [], "targets": { "build": { @@ -11,7 +11,7 @@ "outputs": ["{options.outputPath}"], "defaultConfiguration": "development", "options": { - "outputPath": "dist/consumer-service", + "outputPath": "dist/customer-service", "ssr": "src/main.ts", "outputFileName": "main.mjs" }, @@ -28,20 +28,20 @@ "executor": "@nx/js:node", "defaultConfiguration": "development", "options": { - "buildTarget": "consumer-service:build", + "buildTarget": "customer-service:build", "watch": false }, "configurations": { "development": { - "buildTarget": "consumer-service:build:development", + "buildTarget": "customer-service:build:development", "args": ["server:start"], "watch": true }, "production": { - "buildTarget": "consumer-service:build:production" + "buildTarget": "customer-service:build:production" }, "staging": { - "buildTarget": "consumer-service:build:staging" + "buildTarget": "customer-service:build:staging" } } }, @@ -54,7 +54,7 @@ "cache-from": ["type=gha"], "cache-to": ["type=gha,mode=max"], "metadata": { - "images": ["ghcr.io/zapxme/consumer-service"], + "images": ["ghcr.io/zapxme/customer-service"], "tags": [ "type=schedule", "type=ref,event=branch", @@ -69,23 +69,23 @@ "executor": "@nx/js:node", "defaultConfiguration": "development", "options": { - "buildTarget": "consumer-service:build", + "buildTarget": "customer-service:build", "args": [ "migration:create", "--migrationDir", - "consumer-service/src/migrations" + "customer-service/src/migrations" ], "watch": false }, "configurations": { "development": { - "buildTarget": "consumer-service:build:development" + "buildTarget": "customer-service:build:development" }, "production": { - "buildTarget": "consumer-service:build:production" + "buildTarget": "customer-service:build:production" }, "staging": { - "buildTarget": "consumer-service:build:staging" + "buildTarget": "customer-service:build:staging" } } }, diff --git a/customer-service/src/config.ts b/customer-service/src/config.ts new file mode 100644 index 0000000..f60e790 --- /dev/null +++ b/customer-service/src/config.ts @@ -0,0 +1,3 @@ +import { ServiceConfig } from '@ftgo/common'; + +export class CustomerServiceConfig extends ServiceConfig {} diff --git a/customer-service/src/customer.repository.ts b/customer-service/src/customer.repository.ts new file mode 100644 index 0000000..7989cb0 --- /dev/null +++ b/customer-service/src/customer.repository.ts @@ -0,0 +1,4 @@ +import { RestateRepository } from '@ftgo/common'; +import { Customer } from '@ftgo/customer-service-api'; + +export class CustomerRepository extends RestateRepository {} diff --git a/consumer-service/src/consumer.service.ts b/customer-service/src/customer.service.ts similarity index 54% rename from consumer-service/src/consumer.service.ts rename to customer-service/src/customer.service.ts index e9dee5b..53ba558 100644 --- a/consumer-service/src/consumer.service.ts +++ b/customer-service/src/customer.service.ts @@ -3,27 +3,27 @@ import { UUID } from '@deepkit/type'; import { Money, PersonName } from '@ftgo/common'; import { - Consumer, - ConsumerCreatedEvent, - ConsumerNotFound, - ConsumerServiceApi, - ConsumerServiceHandlers, - ConsumerVerificationFailed, -} from '@ftgo/consumer-service-api'; + Customer, + CustomerCreatedEvent, + CustomerNotFound, + CustomerServiceApi, + CustomerServiceHandlers, + CustomerVerificationFailed, +} from '@ftgo/customer-service-api'; -import { ConsumerRepository } from './consumer.repository'; +import { CustomerRepository } from './customer.repository'; -@restate.service() -export class ConsumerService implements ConsumerServiceHandlers { +@restate.service() +export class CustomerService implements CustomerServiceHandlers { constructor( - private readonly consumer: ConsumerRepository, + private readonly consumer: CustomerRepository, private readonly events: RestateEventsPublisher, ) {} @restate.handler() async create(name: PersonName): Promise { const consumer = await this.consumer.create(name); - await this.events.publish([new ConsumerCreatedEvent(consumer)]); + await this.events.publish([new CustomerCreatedEvent(consumer)]); return consumer.id; } @@ -35,9 +35,9 @@ export class ConsumerService implements ConsumerServiceHandlers { ): Promise { const consumer = await this.consumer.find({ id: consumerId }); if (!consumer) { - throw new ConsumerNotFound(consumerId); + throw new CustomerNotFound(consumerId); } // TODO: validation - throw new ConsumerVerificationFailed(consumerId); + throw new CustomerVerificationFailed(consumerId); } } diff --git a/consumer-service/src/main.ts b/customer-service/src/main.ts similarity index 57% rename from consumer-service/src/main.ts rename to customer-service/src/main.ts index 9c670e8..63a8f27 100644 --- a/consumer-service/src/main.ts +++ b/customer-service/src/main.ts @@ -3,26 +3,26 @@ import { FrameworkModule } from '@deepkit/framework'; import { RestateKafkaProducerModule } from 'deepkit-restate/kafka'; import { RestateModule } from 'deepkit-restate'; -import { Consumer } from '@ftgo/consumer-service-api'; +import { Customer } from '@ftgo/customer-service-api'; import { provideDatabase } from '@ftgo/common'; -import { ConsumerServiceConfig } from './config'; -import { ConsumerService } from './consumer.service'; -import { ConsumerRepository } from './consumer.repository'; +import { CustomerServiceConfig } from './config'; +import { CustomerService } from './customer.service'; +import { CustomerRepository } from './customer.repository'; void new App({ - config: ConsumerServiceConfig, + config: CustomerServiceConfig, imports: [ new FrameworkModule(), new RestateModule(), // TODO: should be configurable through RestateModule new RestateKafkaProducerModule({ - clientId: 'consumer-service', + clientId: 'customer-service', brokers: [''], }), ], - controllers: [ConsumerService], - providers: [provideDatabase([Consumer]), ConsumerRepository], + controllers: [CustomerService], + providers: [provideDatabase([Customer]), CustomerRepository], }) .loadConfigFromEnv({ prefix: '' }) .run(); diff --git a/accounting-service/tsconfig.app.json b/customer-service/tsconfig.app.json similarity index 100% rename from accounting-service/tsconfig.app.json rename to customer-service/tsconfig.app.json diff --git a/accounting-service/tsconfig.json b/customer-service/tsconfig.json similarity index 100% rename from accounting-service/tsconfig.json rename to customer-service/tsconfig.json diff --git a/accounting-service/tsconfig.spec.json b/customer-service/tsconfig.spec.json similarity index 100% rename from accounting-service/tsconfig.spec.json rename to customer-service/tsconfig.spec.json diff --git a/accounting-service/vite.config.ts b/customer-service/vite.config.ts similarity index 100% rename from accounting-service/vite.config.ts rename to customer-service/vite.config.ts diff --git a/kitchen-service-api/src/lib/replies.ts b/kitchen-service-api/src/lib/replies.ts index 6ca765a..1a6587d 100644 --- a/kitchen-service-api/src/lib/replies.ts +++ b/kitchen-service-api/src/lib/replies.ts @@ -1,5 +1,11 @@ -export class KitchenTicketNotFound {} +import { UUID } from '@deepkit/type'; -export class KitchenConfirmCreateTicket { +export class TicketNotFound {} + +export class TicketConfirmed { constructor(readonly readyAt: Date) {} } + +export class TicketCreated { + constructor(readonly ticketId: UUID) {} +} diff --git a/kitchen-service-api/src/lib/services.ts b/kitchen-service-api/src/lib/services.ts index c47c2c7..9988725 100644 --- a/kitchen-service-api/src/lib/services.ts +++ b/kitchen-service-api/src/lib/services.ts @@ -8,6 +8,7 @@ import { } from '@ftgo/restaurant-service-api'; import { Ticket, TicketDetails } from './entities'; +import { TicketCreated } from './replies'; export interface KitchenServiceHandlers { createTicket( @@ -15,8 +16,8 @@ export interface KitchenServiceHandlers { orderId: UUID, details: TicketDetails, confirmAwakeableId: string, - ): Promise; - confirmCreateTicket(id: UUID, readyAt: Date): Promise; + ): Promise; + confirmTicket(id: UUID, readyAt: Date): Promise; cancelTicket(id: UUID): Promise; beginCancelTicket(restaurantId: UUID, orderId: UUID): Promise; undoBeginCancelTicket(restaurantId: UUID, orderId: UUID): Promise; diff --git a/kitchen-service/src/kitchen.service.ts b/kitchen-service/src/kitchen.service.ts index b0bb5f3..4211f8a 100644 --- a/kitchen-service/src/kitchen.service.ts +++ b/kitchen-service/src/kitchen.service.ts @@ -7,8 +7,9 @@ import { KitchenServiceHandlers, Ticket, TicketDetails, - KitchenTicketNotFound, - KitchenConfirmCreateTicket, + TicketNotFound, + TicketConfirmed, + TicketCreated, } from '@ftgo/kitchen-service-api'; import { TicketRepository } from './ticket.repository'; @@ -21,24 +22,20 @@ export class KitchenService implements KitchenServiceHandlers { private readonly ctx: RestateServiceContext, ) {} - // @ts-ignore @(restate.event().handler()) async createMenu({ restaurant }: RestaurantCreatedEvent): Promise {} - // @ts-ignore @(restate.event().handler()) async reviseMenu({ restaurant }: RestaurantCreatedEvent): Promise {} @restate.handler() - async beginCancelTicket(restaurantId: UUID, orderId: UUID): Promise { - return Promise.resolve(undefined); - } + async beginCancelTicket(restaurantId: UUID, orderId: UUID): Promise {} @restate.handler() async cancelTicket(id: UUID): Promise { const ticket = await this.ticket.find({ id }); if (!ticket) { - throw new KitchenTicketNotFound(); + throw new TicketNotFound(); } ticket.cancel(); await this.ticket.persist(ticket); @@ -58,16 +55,16 @@ export class KitchenService implements KitchenServiceHandlers { } @restate.handler() - async confirmCreateTicket(id: UUID, readyAt: Date): Promise { + async confirmTicket(id: UUID, readyAt: Date): Promise { const ticket = await this.ticket.find({ id }); if (!ticket) { - throw new KitchenTicketNotFound(); + throw new TicketNotFound(); } ticket.confirm(); await this.ticket.persist(ticket); - await this.ctx.resolveAwakeable( + await this.ctx.resolveAwakeable( ticket.confirmCreateAwakeableId!, - new KitchenConfirmCreateTicket(readyAt), + new TicketConfirmed(readyAt), ); return ticket; } @@ -78,14 +75,15 @@ export class KitchenService implements KitchenServiceHandlers { orderId: UUID, details: TicketDetails, confirmAwakeableId: string, - ): Promise { + ): Promise { // TODO: notify kitchen staff that a ticket has been created - return await this.ticket.create( + const ticket = await this.ticket.create( restaurantId, orderId, details, confirmAwakeableId, ); + return new TicketCreated(ticket); } @restate.handler() diff --git a/order-service-api/src/lib/entities.ts b/order-service-api/src/lib/entities.ts index c538a49..e860282 100644 --- a/order-service-api/src/lib/entities.ts +++ b/order-service-api/src/lib/entities.ts @@ -45,7 +45,7 @@ export class Order { readonly lineItems: OrderLineItems; // constructor( - // readonly consumerId: UUID, + // readonly customerId: UUID, // readonly restaurantId: UUID, // readonly deliveryInformation: Embedded, // readonly lineItems: OrderLineItems, @@ -54,7 +54,7 @@ export class Order { static create(data: JSONPartial): Order { return Object.assign(new Order(), data); // return new Order( - // data.consumerId, + // data.customerId, // data.restaurantId, // data.deliveryInformation, // data.lineItems, @@ -198,7 +198,7 @@ export class OrderLineItem { } export interface OrderDetails { - readonly consumerId: UUID; + readonly customerId: UUID; readonly restaurantId: UUID; readonly lineItems: readonly OrderLineItem[]; readonly orderTotal: Money; diff --git a/order-service-api/src/lib/sagas.ts b/order-service-api/src/lib/sagas.ts index bf1fca2..e13a2ed 100644 --- a/order-service-api/src/lib/sagas.ts +++ b/order-service-api/src/lib/sagas.ts @@ -7,6 +7,8 @@ import { OrderDetails, OrderRevision } from './entities'; export enum CreateOrderSagaState { STARTED = 'STARTED', + CUSTOMER_VALIDATED = 'CUSTOMER_VALIDATED', + PAYMENT_RESERVED = 'PAYMENT_RESERVED', CANCELLED = 'CANCELLED', } @@ -14,8 +16,8 @@ export class CreateOrderSagaData { readonly state: CreateOrderSagaState = CreateOrderSagaState.STARTED; readonly orderId: UUID; readonly orderDetails: OrderDetails; + readonly paymentId?: UUID; readonly ticketId?: UUID; - readonly confirmCreateTicketAwakeableId?: string; } export type CreateOrderSagaApi = RestateSaga< diff --git a/order-service/src/sagas/cancel-order.saga.ts b/order-service/src/sagas/cancel-order.saga.ts index 783e3d4..3af4abf 100644 --- a/order-service/src/sagas/cancel-order.saga.ts +++ b/order-service/src/sagas/cancel-order.saga.ts @@ -1,7 +1,7 @@ import { restate, Saga } from 'deepkit-restate'; import { KitchenServiceApi } from '@ftgo/kitchen-service-api'; -import { AccountingServiceApi } from '@ftgo/accounting-service-api'; +import { PaymentServiceApi } from '@ftgo/payment-service-api'; import { CancelOrderSagaApi, CancelOrderSagaData, @@ -27,7 +27,7 @@ export class CancelOrderSaga extends Saga { constructor( private readonly order: OrderServiceApi, private readonly kitchen: KitchenServiceApi, - private readonly accounting: AccountingServiceApi, + private readonly accounting: PaymentServiceApi, ) { super(); } diff --git a/order-service/src/sagas/create-order.saga.ts b/order-service/src/sagas/create-order.saga.ts index 6edf5b1..80eb5d5 100644 --- a/order-service/src/sagas/create-order.saga.ts +++ b/order-service/src/sagas/create-order.saga.ts @@ -4,34 +4,40 @@ import { RestateSagaContext, Saga, } from 'deepkit-restate'; -import { cast, UUID } from '@deepkit/type'; +import { cast } from '@deepkit/type'; import { Writable } from 'type-fest'; -import { ConsumerServiceApi } from '@ftgo/consumer-service-api'; -import { AccountingServiceApi } from '@ftgo/accounting-service-api'; +import { CustomerServiceApi } from '@ftgo/customer-service-api'; +import { PaymentReserved, PaymentServiceApi } from '@ftgo/payment-service-api'; import { - KitchenConfirmCreateTicket, KitchenServiceApi, Ticket, + TicketConfirmed, + TicketCreated, TicketDetails, } from '@ftgo/kitchen-service-api'; import { CreateOrderSagaApi, CreateOrderSagaData, + CreateOrderSagaState, OrderServiceApi, } from '@ftgo/order-service-api'; @restate.saga() export class CreateOrderSaga extends Saga { - #confirmTicket?: RestateAwakeable; + #confirmTicketAwakeable?: RestateAwakeable; readonly definition = this.step() .compensate(this.reject) .step() .invoke(this.validate) .step() + .invoke(this.reservePayment) + .onReply(this.handlePaymentReserved) + .compensate(this.reversePayment) + .step() .invoke(this.createTicket) - .onReply(this.handleTicketCreated) + .onReply(this.handleTicketCreated) .compensate(this.cancelTicket) .step() .invoke(this.authorize) @@ -42,10 +48,10 @@ export class CreateOrderSaga extends Saga { .build(); constructor( - private readonly consumer: ConsumerServiceApi, + private readonly customer: CustomerServiceApi, private readonly order: OrderServiceApi, private readonly kitchen: KitchenServiceApi, - private readonly accounting: AccountingServiceApi, + private readonly payment: PaymentServiceApi, private readonly ctx: RestateSagaContext, ) { super(); @@ -56,10 +62,33 @@ export class CreateOrderSaga extends Saga { } validate({ - orderDetails: { consumerId, orderTotal }, + orderDetails: { customerId, orderTotal }, + orderId, + }: CreateOrderSagaData) { + // validate that customer can reserve the money + return this.customer.validateOrder(customerId, orderId, orderTotal); + } + + reservePayment({ + orderDetails: { customerId, orderTotal }, orderId, }: CreateOrderSagaData) { - return this.consumer.validateOrder(consumerId, orderId, orderTotal); + return this.payment.reserve(customerId, orderId, orderTotal); + } + + handlePaymentReserved( + data: Writable, + { paymentId }: PaymentReserved, + ) { + data.paymentId = paymentId; + data.state = CreateOrderSagaState.PAYMENT_RESERVED; + } + + reversePayment({ paymentId }: CreateOrderSagaData) { + if (!paymentId) { + throw new Error('Missing payment id'); + } + return this.payment.reverse(paymentId); } async createTicket({ @@ -67,32 +96,39 @@ export class CreateOrderSaga extends Saga { orderId, }: CreateOrderSagaData) { const details = cast({ lineItems }); - this.#confirmTicket = this.ctx.awakeable(); + this.#confirmTicketAwakeable = this.ctx.awakeable(); return this.kitchen.createTicket( restaurantId, orderId, details, - this.#confirmTicket!.id, + this.#confirmTicketAwakeable!.id, ); } - handleTicketCreated(data: Writable, ticket: Ticket) { - data.ticketId = ticket.id; + handleTicketCreated( + data: Writable, + { ticketId }: TicketCreated, + ) { + data.ticketId = ticketId; } - async cancelTicket({ ticketId }: CreateOrderSagaData) { - return this.kitchen.cancelTicket(ticketId!); + cancelTicket({ ticketId }: CreateOrderSagaData) { + if (!ticketId) { + throw new Error('Missing ticket id'); + } + return this.kitchen.cancelTicket(ticketId); } + // TODO: should payment be processed upon ticket confirmation or after delivery? authorize({ - orderDetails: { consumerId, orderTotal }, + orderDetails: { customerId, orderTotal }, orderId, }: CreateOrderSagaData) { - return this.accounting.authorize(consumerId, orderId, orderTotal); + return this.payment.authorize(customerId, orderId, orderTotal); } async waitForTicketConfirmation() { - await this.#confirmTicket!.promise; + await this.#confirmTicketAwakeable!.promise; } approve({ orderId }: CreateOrderSagaData) { diff --git a/consumer-service-api/.eslintrc.json b/payment-service-api/.eslintrc.json similarity index 100% rename from consumer-service-api/.eslintrc.json rename to payment-service-api/.eslintrc.json diff --git a/payment-service-api/README.md b/payment-service-api/README.md new file mode 100644 index 0000000..636076c --- /dev/null +++ b/payment-service-api/README.md @@ -0,0 +1,11 @@ +# payment-service-api + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build payment-service-api` to build the library. + +## Running unit tests + +Run `nx test payment-service-api` to execute the unit tests via [Vitest](https://vitest.dev/). diff --git a/accounting-service-api/package.json b/payment-service-api/package.json similarity index 72% rename from accounting-service-api/package.json rename to payment-service-api/package.json index 94e0c60..a68163b 100644 --- a/accounting-service-api/package.json +++ b/payment-service-api/package.json @@ -1,5 +1,5 @@ { - "name": "@ftgo/accounting-service-api", + "name": "@ftgo/payment-service-api", "version": "0.0.1", "type": "module", "dependencies": { diff --git a/accounting-service-api/project.json b/payment-service-api/project.json similarity index 50% rename from accounting-service-api/project.json rename to payment-service-api/project.json index 7ba573f..f976fab 100644 --- a/accounting-service-api/project.json +++ b/payment-service-api/project.json @@ -1,7 +1,7 @@ { - "name": "accounting-service-api", + "name": "payment-service-api", "$schema": "../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "accounting-service-api/src", + "sourceRoot": "payment-service-api/src", "projectType": "library", "tags": [], "targets": { @@ -9,17 +9,17 @@ "executor": "@nx/js:tsc", "outputs": ["{options.outputPath}"], "options": { - "outputPath": "dist/accounting-service-api", - "main": "accounting-service-api/src/index.ts", - "tsConfig": "accounting-service-api/tsconfig.lib.json", - "assets": ["accounting-service-api/*.md"] + "outputPath": "dist/payment-service-api", + "main": "payment-service-api/src/index.ts", + "tsConfig": "payment-service-api/tsconfig.lib.json", + "assets": ["payment-service-api/*.md"] } }, "test": { "executor": "@nx/vite:test", "outputs": ["{options.reportsDirectory}"], "options": { - "reportsDirectory": "../coverage/accounting-service-api" + "reportsDirectory": "../coverage/payment-service-api" } } } diff --git a/accounting-service-api/src/index.ts b/payment-service-api/src/index.ts similarity index 100% rename from accounting-service-api/src/index.ts rename to payment-service-api/src/index.ts diff --git a/payment-service-api/src/lib/entities.ts b/payment-service-api/src/lib/entities.ts new file mode 100644 index 0000000..fa4292c --- /dev/null +++ b/payment-service-api/src/lib/entities.ts @@ -0,0 +1,6 @@ +import { entity, Unique, uuid, UUID } from '@deepkit/type'; + +@entity.name('payment') +export class Payment { + readonly id: UUID = uuid(); +} diff --git a/payment-service-api/src/lib/replies.ts b/payment-service-api/src/lib/replies.ts new file mode 100644 index 0000000..33f7fb6 --- /dev/null +++ b/payment-service-api/src/lib/replies.ts @@ -0,0 +1,22 @@ +import { UUID } from '@deepkit/type'; + +export class PaymentAuthorized { + constructor(readonly customerId: UUID) {} +} + +export class PaymentReserved { + constructor(readonly paymentId: UUID) {} +} + +export class PaymentReversed { + constructor(readonly paymentId: UUID) {} +} + +export class PaymentAuthorizationFailed extends Error { + constructor( + readonly customerId: UUID, + readonly reason: string, + ) { + super(); + } +} diff --git a/payment-service-api/src/lib/services.ts b/payment-service-api/src/lib/services.ts new file mode 100644 index 0000000..d94e4fd --- /dev/null +++ b/payment-service-api/src/lib/services.ts @@ -0,0 +1,28 @@ +import { RestateService } from 'deepkit-restate'; +import { UUID } from '@deepkit/type'; + +import { CustomerCreatedEvent } from '@ftgo/customer-service-api'; +import { Money } from '@ftgo/common'; + +import { + PaymentAuthorizationFailed, + PaymentAuthorized, + PaymentReserved, +} from './replies'; +import { Payment } from './entities'; + +export interface PaymentServiceHandlers { + createCustomer(event: CustomerCreatedEvent): Promise; + reserve( + customerId: UUID, + orderId: UUID, + orderTotal: Money, + ): Promise; + reverse(paymentId: UUID): Promise; +} + +export type PaymentServiceApi = RestateService< + 'Payment', + PaymentServiceHandlers, + [Payment, PaymentAuthorizationFailed] +>; diff --git a/consumer-service-api/tsconfig.json b/payment-service-api/tsconfig.json similarity index 100% rename from consumer-service-api/tsconfig.json rename to payment-service-api/tsconfig.json diff --git a/consumer-service-api/tsconfig.lib.json b/payment-service-api/tsconfig.lib.json similarity index 100% rename from consumer-service-api/tsconfig.lib.json rename to payment-service-api/tsconfig.lib.json diff --git a/consumer-service-api/tsconfig.spec.json b/payment-service-api/tsconfig.spec.json similarity index 100% rename from consumer-service-api/tsconfig.spec.json rename to payment-service-api/tsconfig.spec.json diff --git a/accounting-service-api/vite.config.ts b/payment-service-api/vite.config.ts similarity index 81% rename from accounting-service-api/vite.config.ts rename to payment-service-api/vite.config.ts index ff00450..8cf7d50 100644 --- a/accounting-service-api/vite.config.ts +++ b/payment-service-api/vite.config.ts @@ -4,7 +4,7 @@ import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; export default defineConfig({ root: __dirname, - cacheDir: '../node_modules/.vite/accounting-service-api', + cacheDir: '../node_modules/.vite/payment-service-api', plugins: [nxViteTsPaths()], @@ -20,7 +20,7 @@ export default defineConfig({ include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], reporters: ['default'], coverage: { - reportsDirectory: '../coverage/accounting-service-api', + reportsDirectory: '../coverage/payment-service-api', provider: 'v8', }, }, diff --git a/consumer-service/.eslintrc.json b/payment-service/.eslintrc.json similarity index 100% rename from consumer-service/.eslintrc.json rename to payment-service/.eslintrc.json diff --git a/accounting-service/Dockerfile b/payment-service/Dockerfile similarity index 80% rename from accounting-service/Dockerfile rename to payment-service/Dockerfile index cf54c40..70a08e1 100644 --- a/accounting-service/Dockerfile +++ b/payment-service/Dockerfile @@ -6,6 +6,6 @@ RUN bun install --production FROM scratch ENV NODE_ENV production COPY --from=deps /usr/src/node_modules /usr/src/node_modules -COPY ../dist/accounting-service/main.mjs /usr/src/server.mjs +COPY ../dist/payment-service/main.mjs /usr/src/server.mjs CMD ["/usr/bin/node", "/usr/src/server.mjs", "server:start"] diff --git a/accounting-service/project.json b/payment-service/project.json similarity index 71% rename from accounting-service/project.json rename to payment-service/project.json index 4968777..a5b3f78 100644 --- a/accounting-service/project.json +++ b/payment-service/project.json @@ -1,9 +1,9 @@ { - "name": "accounting-service", + "name": "payment-service", "$schema": "../node_modules/nx/schemas/project-schema.json", "projectType": "application", "prefix": "ftgo", - "sourceRoot": "accounting-service/src", + "sourceRoot": "payment-service/src", "tags": [], "targets": { "build": { @@ -11,7 +11,7 @@ "outputs": ["{options.outputPath}"], "defaultConfiguration": "development", "options": { - "outputPath": "dist/accounting-service", + "outputPath": "dist/payment-service", "ssr": "src/main.ts", "outputFileName": "main.mjs" }, @@ -28,26 +28,26 @@ "executor": "@nx/js:node", "defaultConfiguration": "development", "options": { - "buildTarget": "accounting-service:build", + "buildTarget": "payment-service:build", "watch": false }, "configurations": { "development": { - "buildTarget": "accounting-service:build:development", + "buildTarget": "payment-service:build:development", "args": ["server:start"], "watch": true }, "production": { - "buildTarget": "accounting-service:build:production" + "buildTarget": "payment-service:build:production" }, "staging": { - "buildTarget": "accounting-service:build:staging" + "buildTarget": "payment-service:build:staging" } } }, "deploy": { "dependsOn": ["build"], - "command": "node scripts/deploy-kraft-service.mjs accounting-service {projectRoot}" + "command": "node scripts/deploy-kraft-service.mjs payment-service {projectRoot}" }, "container": { "executor": "@nx-tools/nx-container:build", @@ -58,7 +58,7 @@ "cache-from": ["type=gha"], "cache-to": ["type=gha,mode=max"], "metadata": { - "images": ["ghcr.io/ftgo/accounting-service"], + "images": ["ghcr.io/ftgo/payment-service"], "tags": [ "type=schedule", "type=ref,event=branch", @@ -73,23 +73,23 @@ "executor": "@nx/js:node", "defaultConfiguration": "development", "options": { - "buildTarget": "accounting-service:build", + "buildTarget": "payment-service:build", "args": [ "migration:create", "--migrationDir", - "accounting-service/src/migrations" + "payment-service/src/migrations" ], "watch": false }, "configurations": { "development": { - "buildTarget": "accounting-service:build:development" + "buildTarget": "payment-service:build:development" }, "production": { - "buildTarget": "accounting-service:build:production" + "buildTarget": "payment-service:build:production" }, "staging": { - "buildTarget": "accounting-service:build:staging" + "buildTarget": "payment-service:build:staging" } } }, diff --git a/payment-service/src/config.ts b/payment-service/src/config.ts new file mode 100644 index 0000000..587dd92 --- /dev/null +++ b/payment-service/src/config.ts @@ -0,0 +1,3 @@ +import { ServiceConfig } from '@ftgo/common'; + +export class PaymentServiceConfig extends ServiceConfig {} diff --git a/payment-service/src/main.ts b/payment-service/src/main.ts new file mode 100644 index 0000000..3bc8d19 --- /dev/null +++ b/payment-service/src/main.ts @@ -0,0 +1,19 @@ +import { App } from '@deepkit/app'; +import { FrameworkModule } from '@deepkit/framework'; +import { RestateModule } from 'deepkit-restate'; + +import { provideDatabase } from '@ftgo/common'; +import { Payment } from '@ftgo/payment-service-api'; + +import { PaymentServiceConfig } from './config'; +import { PaymentService } from './payment.service'; +import { PaymentRepository } from './payment.repository'; + +void new App({ + config: PaymentServiceConfig, + imports: [new FrameworkModule(), new RestateModule()], + controllers: [PaymentService], + providers: [provideDatabase([Payment]), PaymentRepository], +}) + .loadConfigFromEnv({ prefix: '' }) + .run(); diff --git a/accounting-service/src/account.repository.ts b/payment-service/src/payment.repository.ts similarity index 57% rename from accounting-service/src/account.repository.ts rename to payment-service/src/payment.repository.ts index 01cf820..653a610 100644 --- a/accounting-service/src/account.repository.ts +++ b/payment-service/src/payment.repository.ts @@ -2,16 +2,16 @@ import { UUID } from '@deepkit/type'; import { DatabaseQueryModel } from '@deepkit/orm'; import { RestateRepository } from '@ftgo/common'; -import { Account, AccountNotFound } from '@ftgo/accounting-service-api'; +import { Payment, AccountNotFound } from '@ftgo/payment-service-api'; -export class AccountRepository extends RestateRepository { - async findByConsumer(id: UUID): Promise { +export class PaymentRepository extends RestateRepository { + async findByConsumer(id: UUID): Promise { return await this.find({ consumerId: id }); } override async find( - filter: DatabaseQueryModel['filter'], - ): Promise { + filter: DatabaseQueryModel['filter'], + ): Promise { const account = await super.find(filter); if (!account) { throw new AccountNotFound(); diff --git a/payment-service/src/payment.service.ts b/payment-service/src/payment.service.ts new file mode 100644 index 0000000..2993752 --- /dev/null +++ b/payment-service/src/payment.service.ts @@ -0,0 +1,30 @@ +import { restate } from 'deepkit-restate'; +import { UUID } from '@deepkit/type'; + +import { CustomerCreatedEvent } from '@ftgo/customer-service-api'; +import { Money } from '@ftgo/common'; +import { + PaymentReserved, + PaymentServiceApi, + PaymentServiceHandlers, +} from '@ftgo/payment-service-api'; + +import { PaymentRepository } from './payment.repository'; + +@restate.service() +export class PaymentService implements PaymentServiceHandlers { + constructor(private readonly payment: PaymentRepository) {} + + @(restate.event().handler()) + async createCustomer({ customer }: CustomerCreatedEvent): Promise {} + + @restate.handler() + async reserve( + customerId: UUID, + orderId: UUID, + orderTotal: Money, + ): Promise {} + + @restate.handler() + async reverse(paymentId: UUID): Promise {} +} diff --git a/consumer-service/tsconfig.app.json b/payment-service/tsconfig.app.json similarity index 100% rename from consumer-service/tsconfig.app.json rename to payment-service/tsconfig.app.json diff --git a/consumer-service/tsconfig.json b/payment-service/tsconfig.json similarity index 100% rename from consumer-service/tsconfig.json rename to payment-service/tsconfig.json diff --git a/consumer-service/tsconfig.spec.json b/payment-service/tsconfig.spec.json similarity index 100% rename from consumer-service/tsconfig.spec.json rename to payment-service/tsconfig.spec.json diff --git a/consumer-service/vite.config.ts b/payment-service/vite.config.ts similarity index 100% rename from consumer-service/vite.config.ts rename to payment-service/vite.config.ts diff --git a/tsconfig.base.json b/tsconfig.base.json index cad5a5c..1132e7a 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -26,9 +26,9 @@ "baseUrl": ".", "types": ["node"], "paths": { - "@ftgo/accounting-service-api": ["accounting-service-api/src/index.ts"], + "@ftgo/payment-service-api": ["payment-service-api/src/index.ts"], "@ftgo/common": ["common/src/index.ts"], - "@ftgo/consumer-service-api": ["consumer-service-api/src/index.ts"], + "@ftgo/customer-service-api": ["customer-service-api/src/index.ts"], "@ftgo/delivery-service-api": ["delivery-service-api/src/index.ts"], "@ftgo/kitchen-service-api": ["kitchen-service-api/src/index.ts"], "@ftgo/order-service-api": ["order-service-api/src/index.ts"],