Skip to content

Commit

Permalink
feat(demo): use the lib and improve little things
Browse files Browse the repository at this point in the history
  • Loading branch information
Plopix committed Nov 1, 2024
1 parent 5e3634a commit b40239d
Show file tree
Hide file tree
Showing 22 changed files with 678 additions and 217 deletions.
5 changes: 4 additions & 1 deletion demo/fancy-demo-one/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@
"typecheck": "tsc"
},
"dependencies": {
"@faker-js/faker": "^9.1.0",
"@libsql/client": "^0.14.0",
"@remix-run/express": "^2.13.1",
"@remix-run/node": "^2.13.1",
"@remix-run/react": "^2.13.1",
"awilix": "^12.0.3",
"compression": "^1.7.4",
"compression": "^1.7.5",
"dotenv": "^16.4.5",
"drizzle-orm": "^0.36.0",
"express": "^4.21.1",
Expand All @@ -26,7 +27,9 @@
"node-cache": "^5.1.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"remix-utils": "^7.7.0",
"signale": "^1.4.0",
"superjson": "^2.2.1",
"zod": "^3.23.8"
},
"devDependencies": {
Expand Down
1 change: 1 addition & 0 deletions demo/fancy-demo-one/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const viteDevServer = isProduction

declare module '@remix-run/node' {
interface AppLoadContext {
services: ReturnType<typeof buildContainer>['cradle'];
logger: ReturnType<typeof buildContainer>['cradle']['logger'];
createQuery: ReturnType<typeof buildContainer>['cradle']['queryBus']['createQuery'];
dispatchQuery: ReturnType<typeof buildContainer>['cradle']['queryBus']['dispatch'];
Expand Down
58 changes: 50 additions & 8 deletions demo/fancy-demo-one/src/core/container.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@ import {
} from '~/domain/contracts/bus';
import { drizzle } from 'drizzle-orm/libsql';
import {
createListAllCharacterHandler,
createListAllCharactersHandler,
ListAllCharactersQuerySchema,
} from '~/domain/use-cases/read/list-all-characters.server';
import { DefaultLogger } from 'drizzle-orm/logger';
import { AddCharacterCommandSchema, createAddCharacterHandler } from '~/domain/use-cases/write/add-character.server';
import { AddQuestCommandSchema, createAddQuestHandler } from '~/domain/use-cases/write/add-quest.server';
import { createListAllQuestsHandler, ListAllQuestsQuerySchema } from '~/domain/use-cases/read/list-all-quests.server';
import { EventEmitter } from 'events';
import { createMemoryStorage } from '~/infrastructure/create-memory-storage.server';

export const buildContainer = () => {
const logger = createLogger(['info', 'debug']);
Expand All @@ -24,43 +29,80 @@ export const buildContainer = () => {
connection: { url: process.env.DB_FILE_NAME! },
logger: dLogger,
});
const emitter = new EventEmitter();
const container = createContainer<{
logger: Logger;
queryBus: QueryBus;
commandBus: CommandBus;
eventBus: EventBus;
db: typeof db;
listAllCharactersQueryHandler: ReturnType<typeof createListAllCharacterHandler>;
emitter: typeof emitter;
listAllCharactersQueryHandler: ReturnType<typeof createListAllCharactersHandler>;
listAllQuestsQueryHandler: ReturnType<typeof createListAllQuestsHandler>;
addCharacterCommandHandler: ReturnType<typeof createAddCharacterHandler>;
addQuestCommandHandler: ReturnType<typeof createAddQuestHandler>;
busObsName: string;
}>({
injectionMode: InjectionMode.PROXY,
strict: true,
});

container.register({
logger: asValue(logger),
emitter: asValue(emitter),
queryBus: asFunction(() => createQueryBus<QueryDefitions>()).singleton(),
commandBus: asFunction(() => createCommandBus<CommandDefitions>()).singleton(),
eventBus: asFunction(() => createEventBus<EventDefitions>()).singleton(),
listAllCharactersQueryHandler: asFunction(createListAllCharacterHandler).singleton(),
listAllCharactersQueryHandler: asFunction(createListAllCharactersHandler).singleton(),
addCharacterCommandHandler: asFunction(createAddCharacterHandler).singleton(),
listAllQuestsQueryHandler: asFunction(createListAllQuestsHandler).singleton(),
addQuestCommandHandler: asFunction(createAddQuestHandler).singleton(),
db: asValue(db),
busObsName: asValue('bus-obs'),
});

const memoryStorage = createMemoryStorage({
prefix: 'missive-demo-query-bus',
});

// Query Bus
const loggerAdapter = { log: container.cradle.logger.info, error: container.cradle.logger.error };
container.cradle.queryBus.useLoggerMiddleware({ logger: loggerAdapter });
const simpleLogger = { log: container.cradle.logger.info, error: container.cradle.logger.error };
const observabilityLogger = {
log: (...args: unknown[]) => {
emitter.emit(container.cradle.busObsName, ['query', ...args]);
},
error: (...args: unknown[]) => {},
};

container.cradle.queryBus.useCacherMiddleware({
defaultTtl: 10,
adapter: memoryStorage,
defaultTtl: 20,
});
container.cradle.queryBus.useLoggerMiddleware({ logger: observabilityLogger });
container.cradle.queryBus.useLoggerMiddleware({ logger: simpleLogger });

container.cradle.queryBus.register(
'ListAllCharacters',
ListAllCharactersQuerySchema,
container.cradle.listAllCharactersQueryHandler,
);
container.cradle.queryBus.register(
'ListAllQuests',
ListAllQuestsQuerySchema,
container.cradle.listAllQuestsQueryHandler,
);

// Command Bus
container.cradle.commandBus.useLoggerMiddleware({ logger: loggerAdapter });
container.cradle.commandBus.useLoggerMiddleware({ logger: observabilityLogger });
container.cradle.commandBus.useLoggerMiddleware({ logger: simpleLogger });
container.cradle.commandBus.register(
'AddCharacter',
AddCharacterCommandSchema,
container.cradle.addCharacterCommandHandler,
);
container.cradle.commandBus.register('AddQuest', AddQuestCommandSchema, container.cradle.addQuestCommandHandler);

// Event Bus
container.cradle.eventBus.useLoggerMiddleware({ logger: loggerAdapter });
container.cradle.eventBus.useLoggerMiddleware({ logger: simpleLogger });
return container;
};
9 changes: 6 additions & 3 deletions demo/fancy-demo-one/src/domain/contracts/bus.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { QueryBus as MissiveQueryBus, CommandBus as MissiveCommandBus, EventBus as MissiveEventBus } from 'missive.js';
import { ListAllCharacterHandlerDefinition } from '../use-cases/read/list-all-characters.server';
import { ListAllCharactersHandlerDefinition } from '../use-cases/read/list-all-characters.server';
import { AddCharacterHandlerDefinition } from '../use-cases/write/add-character.server';
import { AddQuestHandlerDefinition } from '../use-cases/write/add-quest.server';
import { ListAllQuestsHandlerDefinition } from '../use-cases/read/list-all-quests.server';

export type QueryDefitions = ListAllCharacterHandlerDefinition;
export type QueryDefitions = ListAllCharactersHandlerDefinition & ListAllQuestsHandlerDefinition;
export type QueryBus = MissiveQueryBus<QueryDefitions>;

export type CommandDefitions = any;
export type CommandDefitions = AddCharacterHandlerDefinition & AddQuestHandlerDefinition;
export type CommandBus = MissiveCommandBus<CommandDefitions>;

export type EventDefitions = any;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,10 @@ export const ListAllCharactersQuerySchema = z.object({});
type Query = z.infer<typeof ListAllCharactersQuerySchema>;
type Result = Awaited<ReturnType<typeof handler>>;

export type ListAllCharacterHandlerDefinition = QueryHandlerDefinition<'ListAllCharacters', Query, Result>;
export type ListAllCharactersHandlerDefinition = QueryHandlerDefinition<'ListAllCharacters', Query, Result>;

const handler = async (envelope: Envelope<Query>, { db }: Deps) => {
const items = await db.select().from(characters).all();
return {
success: true,
items,
};
return items;
};
export const createListAllCharacterHandler = (deps: Deps) => (query: Envelope<Query>) => handler(query, deps);
export const createListAllCharactersHandler = (deps: Deps) => (query: Envelope<Query>) => handler(query, deps);
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { drizzle } from 'drizzle-orm/libsql';
import { Envelope, QueryHandlerDefinition } from 'missive.js';
import { z } from 'zod';
import { quests } from '~/db/schema';

type Deps = {
db: ReturnType<typeof drizzle>;
};

export const ListAllQuestsQuerySchema = z.object({});
type Query = z.infer<typeof ListAllQuestsQuerySchema>;
type Result = Awaited<ReturnType<typeof handler>>;

export type ListAllQuestsHandlerDefinition = QueryHandlerDefinition<'ListAllQuests', Query, Result>;

const handler = async (envelope: Envelope<Query>, { db }: Deps) => {
const items = await db.select().from(quests).all();
return items;
};
export const createListAllQuestsHandler = (deps: Deps) => (query: Envelope<Query>) => handler(query, deps);
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { faker } from '@faker-js/faker';
import { drizzle } from 'drizzle-orm/libsql';
import { Envelope, CommandHandlerDefinition } from 'missive.js';
import { z } from 'zod';
import { characters } from '~/db/schema';

type Deps = {
db: ReturnType<typeof drizzle>;
};

export const AddCharacterCommandSchema = z.object({
name: z.string(),
});
type Command = z.infer<typeof AddCharacterCommandSchema>;
type Result = Awaited<ReturnType<typeof handler>>;

export type AddCharacterHandlerDefinition = CommandHandlerDefinition<'AddCharacter', Command, Result>;

const handler = async (envelope: Envelope<Command>, { db }: Deps) => {
const { name } = envelope.message;
const type = faker.helpers.arrayElement([
'bear',
'bird',
'cat',
'dog',
'cetacean',
'cow',
'crocodilia',
'dog',
'fish',
'horse',
'rabbit',
'snake',
'rodent',
'insect',
]);

const characterClass = faker.animal[type]();
const character = {
name,
class: characterClass,
agility: Math.floor(Math.random() * 50) + 1,
strength: Math.floor(Math.random() * 50) + 1,
magic: Math.floor(Math.random() * 50) + 1,
level: 1,
experience: 0,
retired: false,
};
const result = await db.insert(characters).values(character);
return {
...character,
id: result.lastInsertRowid?.toString(),
};
};
export const createAddCharacterHandler = (deps: Deps) => (query: Envelope<Command>) => handler(query, deps);
34 changes: 34 additions & 0 deletions demo/fancy-demo-one/src/domain/use-cases/write/add-quest.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { faker } from '@faker-js/faker';
import { drizzle } from 'drizzle-orm/libsql';
import { Envelope, CommandHandlerDefinition } from 'missive.js';
import { z } from 'zod';
import { quests } from '~/db/schema';

type Deps = {
db: ReturnType<typeof drizzle>;
};

export const AddQuestCommandSchema = z.object({
title: z.string(),
});
type Command = z.infer<typeof AddQuestCommandSchema>;
type Result = Awaited<ReturnType<typeof handler>>;

export type AddQuestHandlerDefinition = CommandHandlerDefinition<'AddQuest', Command, Result>;

const handler = async (envelope: Envelope<Command>, { db }: Deps) => {
const { title } = envelope.message;
const quest = {
title,
description: `${faker.book.title()} with ${faker.music.songName()}`,
difficulty: faker.helpers.arrayElement(['easy', 'medium', 'hard', 'epic']),
reward: Math.floor(Math.random() * 500) + 1,
completed: false,
};
const result = await db.insert(quests).values(quest);
return {
...quest,
id: result.lastInsertRowid?.toString(),
};
};
export const createAddQuestHandler = (deps: Deps) => (query: Envelope<Command>) => handler(query, deps);
Loading

0 comments on commit b40239d

Please sign in to comment.