Hexagonal, mejora del cliente RMQ y tipos de eventos
This commit is contained in:
21
packages/shared/domain/DomainEvent.ts
Normal file
21
packages/shared/domain/DomainEvent.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Los eventos de dominion estan orientados a la cola AMQ
|
||||
*
|
||||
*/
|
||||
// Completar con los tipos de evento
|
||||
export type DomainEventType = string
|
||||
|
||||
export type DomainEvent = {
|
||||
key: string,
|
||||
payload: Object,
|
||||
options: Object,
|
||||
occurredOn: Date,
|
||||
}
|
||||
|
||||
export interface DomainEventSubscriber<T extends DomainEvent> {
|
||||
subscribedTo(): DomainEventType[];
|
||||
getEventNames(): string[];
|
||||
on(domainEvent: T): Promise<void>;
|
||||
}
|
||||
|
||||
|
||||
6
packages/shared/domain/EventBus.port.ts
Normal file
6
packages/shared/domain/EventBus.port.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { DomainEvent, DomainEventSubscriber } from "./DomainEvent";
|
||||
|
||||
export interface EventBus {
|
||||
publish(events: Array<DomainEvent>): Promise<void>;
|
||||
addSubscribers(subscribers: Array<DomainEventSubscriber<DomainEvent>>): void;
|
||||
}
|
||||
39
packages/shared/domain/SimEvents.ts
Normal file
39
packages/shared/domain/SimEvents.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { DomainEvent } from "./DomainEvent";
|
||||
|
||||
export namespace SimEvents {
|
||||
export type activation = DomainEvent & {
|
||||
key: "sim.activation",
|
||||
payload: {
|
||||
iccid: string
|
||||
},
|
||||
options: {
|
||||
}
|
||||
}
|
||||
|
||||
export type cancelation = DomainEvent & {
|
||||
key: "sim.cancelation",
|
||||
payload: {
|
||||
iccid: string
|
||||
},
|
||||
options: {
|
||||
}
|
||||
}
|
||||
|
||||
export type pause = DomainEvent & {
|
||||
key: "sim.pause",
|
||||
payload: {
|
||||
iccid: string
|
||||
},
|
||||
options: {
|
||||
}
|
||||
}
|
||||
|
||||
export type free = DomainEvent & {
|
||||
key: "sim.free",
|
||||
payload: {
|
||||
iccid: string
|
||||
},
|
||||
options: {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,56 @@
|
||||
import client, { ChannelModel, ConfirmChannel, connect, Connection } from "amqplib"
|
||||
import { type ChannelModel, type ConfirmChannel, connect as amqConnect } from "amqplib";
|
||||
import { DomainEvent, DomainEventSubscriber } from "../domain/DomainEvent";
|
||||
import { EventBus } from "../domain/EventBus.port";
|
||||
import { buffer } from "node:stream/consumers";
|
||||
|
||||
export type RMQConnectionParams = {
|
||||
username: string,
|
||||
password: string,
|
||||
vhost: string,
|
||||
hostname: string,
|
||||
port: number,
|
||||
secure: boolean
|
||||
}
|
||||
|
||||
const PREFETCH_LIMIT = 1
|
||||
export class RabbitMQEventBus implements EventBus {
|
||||
constructor(args: {
|
||||
connectionParams: RMQConnectionParams
|
||||
}) {
|
||||
this.connectionOptions = args.connectionParams
|
||||
this.checkStructure();
|
||||
}
|
||||
|
||||
export class RabbitConnection {
|
||||
connection?: ChannelModel
|
||||
channel?: ConfirmChannel
|
||||
connected: Boolean = false
|
||||
|
||||
private connectionOptions: {
|
||||
hostname: string,
|
||||
port: number,
|
||||
secure: boolean,
|
||||
username: string,
|
||||
password: string,
|
||||
vhost: string
|
||||
private connectionOptions: RMQConnectionParams
|
||||
|
||||
public async connect() {
|
||||
this.connection = await this.createConnection();
|
||||
if (this.connection == undefined) throw new Error("[RMQ] Error crecreando la conexion")
|
||||
this.channel = await this.createConfirmChannel()
|
||||
}
|
||||
|
||||
constructor(opts: typeof this.connectionOptions) {
|
||||
this.connectionOptions = opts
|
||||
this.checkStructure();
|
||||
publish(events: DomainEvent[]): Promise<void> {
|
||||
return new Promise((res, rej) => {
|
||||
try {
|
||||
for (const event of events) {
|
||||
const exchange = "sim.exchange"
|
||||
const routingKey = event.key
|
||||
const content = Buffer.from(JSON.stringify(event))
|
||||
this.channel?.publish(exchange, routingKey, content)
|
||||
}
|
||||
return res()
|
||||
} catch (err) {
|
||||
return rej(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
addSubscribers(subscribers: Array<DomainEventSubscriber<DomainEvent>>): void {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,19 +63,13 @@ export class RabbitConnection {
|
||||
this.channel?.assertExchange("sim.exchange", "direct")
|
||||
}
|
||||
|
||||
public async connect() {
|
||||
this.connection = await this.createConnection();
|
||||
if (this.connection == undefined) throw new Error("[RMQ] Error crecreando la conexion")
|
||||
this.channel = await this.createConfirmChannel()
|
||||
}
|
||||
|
||||
protected async createConnection() {
|
||||
const { hostname, port, secure } = { ...this.connectionOptions }
|
||||
const { username, password } = { ...this.connectionOptions };
|
||||
const protocol = secure ? 'amqps' : 'amqp';
|
||||
const vhost = this.connectionOptions.vhost
|
||||
|
||||
const connection = await client.connect({
|
||||
const connection = await amqConnect({
|
||||
protocol,
|
||||
hostname,
|
||||
port,
|
||||
@@ -75,4 +101,3 @@ export class RabbitConnection {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
1
packages/sim-consumidor/config/env/index.ts
vendored
1
packages/sim-consumidor/config/env/index.ts
vendored
@@ -21,4 +21,3 @@ export const env = {
|
||||
RABBITMQ_VHOST: String(process.env.RABBITMQ_VHOST),
|
||||
};
|
||||
|
||||
console.log("Consumidor", env)
|
||||
|
||||
@@ -11,7 +11,6 @@ const rmqVhost = env.RABBITMQ_VHOST
|
||||
|
||||
|
||||
async function test() {
|
||||
console.log("iniciado Consumidor", env)
|
||||
const rbmq = new RabbitConnection({
|
||||
username: rmqUser,
|
||||
password: rmqPass,
|
||||
@@ -26,6 +25,8 @@ async function test() {
|
||||
await rbmq.channel?.consume("sim.queue", (buff: ConsumeMessage | null) => {
|
||||
const decoded = buff?.content.toString()
|
||||
console.log(" [Consumidor] Mensaje recibido ", decoded)
|
||||
}, {
|
||||
noAck: true
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
42
packages/sim-entrada-eventos/aplication/Sim.usecases.ts
Normal file
42
packages/sim-entrada-eventos/aplication/Sim.usecases.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { EventBus } from "../../shared/domain/EventBus.port";
|
||||
import { SimEvents } from "../../shared/domain/SimEvents";
|
||||
|
||||
/**
|
||||
* TODO:
|
||||
* - Conexion con la BDD
|
||||
* - Conexion con RabbitMQ
|
||||
* - Pasar a clase cuando existan las conexiones
|
||||
*/
|
||||
export class SimUsecases {
|
||||
private eventBus: EventBus
|
||||
|
||||
constructor(args: {
|
||||
eventBus: EventBus
|
||||
}
|
||||
) {
|
||||
this.eventBus = args.eventBus
|
||||
}
|
||||
|
||||
async activation(args: { iccid: string }) {
|
||||
const activationEvent = <SimEvents.activation>{
|
||||
key: "sim.activation",
|
||||
payload: {
|
||||
iccid: args.iccid
|
||||
}
|
||||
}
|
||||
|
||||
return this.eventBus.publish([activationEvent])
|
||||
}
|
||||
|
||||
cancelation(args: { iccid: string }) {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
|
||||
pause(args: { iccid: string }) {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
free(args: { iccid: string }) {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
37
packages/sim-entrada-eventos/aplication/SimController.ts
Normal file
37
packages/sim-entrada-eventos/aplication/SimController.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Request, Response } from "express"
|
||||
import { SimUsecases } from "aplication/Sim.usecases"
|
||||
|
||||
export class SimController {
|
||||
private simUseCases: SimUsecases
|
||||
|
||||
constructor(args: {
|
||||
simUseCases: SimUsecases
|
||||
}) {
|
||||
this.simUseCases = args.simUseCases
|
||||
}
|
||||
|
||||
async activation(req: Request, res: Response) {
|
||||
const { iccid } = req.body
|
||||
|
||||
if (iccid == undefined) {
|
||||
// TODO: excepcion con nombre se va a repetir
|
||||
res.status(400).json({
|
||||
msg: "iccid invalido"
|
||||
})
|
||||
}
|
||||
|
||||
const resp = await this.simUseCases.activation({ iccid })
|
||||
}
|
||||
|
||||
cancelation(req: Request, res: Response) {
|
||||
|
||||
}
|
||||
|
||||
pause(req: Request, res: Response) {
|
||||
|
||||
}
|
||||
|
||||
free(req: Request, res: Response) {
|
||||
|
||||
}
|
||||
}
|
||||
50
packages/sim-entrada-eventos/index.ts
Normal file
50
packages/sim-entrada-eventos/index.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { env } from "#config/env"
|
||||
import { SimEvents } from "#shared/domain/SimEvents"
|
||||
import { RabbitMQEventBus, RMQConnectionParams } from "#shared/infrastructure/RabbitMQEventBus"
|
||||
|
||||
const rmqUser = env.RABBITMQ_USER
|
||||
const rmqPass = env.RABBITMQ_PASSWORD
|
||||
const rmqHost = env.RABBITMQ_HOST
|
||||
const rmqPort = Number(env.RABBITMQ_PORT)
|
||||
const rmqSecure = false
|
||||
const rmqVhost = env.RABBITMQ_VHOST
|
||||
|
||||
|
||||
|
||||
async function test() {
|
||||
const connOptions = <RMQConnectionParams>{
|
||||
username: rmqUser,
|
||||
password: rmqPass,
|
||||
vhost: rmqVhost,
|
||||
hostname: rmqHost,
|
||||
port: rmqPort,
|
||||
secure: rmqSecure,
|
||||
}
|
||||
|
||||
const event = <SimEvents.activation>{
|
||||
key: "sim.activation",
|
||||
payload: {
|
||||
iccid: "1234"
|
||||
},
|
||||
options: {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const rmqCli = new RabbitMQEventBus({
|
||||
connectionParams: connOptions
|
||||
})
|
||||
|
||||
await rmqCli.connect()
|
||||
|
||||
console.log("publicando", event)
|
||||
rmqCli.publish([event])
|
||||
.then(e => {
|
||||
console.log("Mensaje publicado", e)
|
||||
})
|
||||
.catch(err => console.error)
|
||||
}
|
||||
|
||||
test()
|
||||
|
||||
export default {}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Router } from 'express';
|
||||
|
||||
const simRoutes = Router()
|
||||
|
||||
simRoutes.get("/status")
|
||||
|
||||
simRoutes.post("/save", (req, res) => {
|
||||
|
||||
})
|
||||
simRoutes.post("/activate", (req, res) => {
|
||||
const { iccid } = req.body
|
||||
})
|
||||
simRoutes.post("/pause", (req, res) => {
|
||||
})
|
||||
|
||||
simRoutes.post("/cancel", (req, res) => {
|
||||
})
|
||||
|
||||
// Proceso especifico de ALAI para liberar sims canceladas
|
||||
simRoutes.post("/free", (req, res) => {
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "sim-gestor-eventos",
|
||||
"name": "sim-entrada-eventos",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.ts",
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/sim-gestor-eventos",
|
||||
"outDir": "../../dist/sim-entrada-eventos",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"#config/*": [
|
||||
@@ -1,42 +0,0 @@
|
||||
import { env } from "#config/env"
|
||||
import { RabbitConnection } from "#shared/adapters/queues/RabbitMQClient"
|
||||
|
||||
const rmqUser = env.RABBITMQ_USER
|
||||
const rmqPass = env.RABBITMQ_PASSWORD
|
||||
const rmqHost = env.RABBITMQ_HOST
|
||||
const rmqPort = Number(env.RABBITMQ_PORT)
|
||||
const rmqSecure = false
|
||||
const rmqVhost = env.RABBITMQ_VHOST
|
||||
|
||||
|
||||
console.log("[Generador] Iniciando")
|
||||
|
||||
async function test() {
|
||||
|
||||
const rbmq = new RabbitConnection({
|
||||
username: rmqUser,
|
||||
password: rmqPass,
|
||||
vhost: String(rmqVhost),
|
||||
hostname: rmqHost,
|
||||
port: rmqPort,
|
||||
secure: rmqSecure
|
||||
})
|
||||
|
||||
await rbmq.connect()
|
||||
|
||||
console.log("[Generador] enviando --")
|
||||
|
||||
rbmq.channel?.sendToQueue("sim.queue", Buffer.from("test"), {},
|
||||
function (err, ok) {
|
||||
if (err !== null)
|
||||
console.warn('Message nacked!');
|
||||
else
|
||||
console.log('Message acked');
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
test()
|
||||
|
||||
export default {}
|
||||
Reference in New Issue
Block a user