Logo

dev-resources.site

for different kinds of informations.

CQRS with Low-Code

Published at
11/27/2023
Categories
cqrs
lowcode
typescript
nestjs
Author
massimobiagioli
Categories
4 categories in total
cqrs
open
lowcode
open
typescript
open
nestjs
open
Author
15 person written this
massimobiagioli
open
CQRS with Low-Code

Bridging the Gap Between Two Worlds

In the realm of software design, marrying Command Query Responsibility Segregation (CQRS) with low-code might seem like an odd match. However, beneath the surface, these two approaches complement each other surprisingly well. This article explores the synergy between CQRS and low-code, demonstrating how their coexistence bridges the gap between traditional and modern software architectures. Join us as we unravel the simplicity and effectiveness of this powerful combination for building robust applications.

The Central Role of Commands in CQRS

In the realm of CQRS, a "command" represents an imperative instruction, typically responsible for altering the state of an application. Commands carry the essence of change, from updating data to triggering specific actions. When it comes to integrating CQRS with a low-code pipeline, the brilliance lies in the ability to delegate command execution. Low-code platforms excel at handling the intricacies of command processing, offering a streamlined and visual approach to define, execute, and manage these imperative actions. This synergy not only simplifies the implementation of commands but also empowers developers to harness the efficiency of low-code for rapid and visual command orchestration.

Command Handling Simplified: The Low-Code Connection

In the world of low-code, simplicity meets power as it takes on the role of a command handler through the orchestration of a dedicated pipeline. Low-code platforms serve as adept command executors, effortlessly managing the flow of instructions through a visual pipeline. This approach not only abstracts the complexities of command execution but also empowers developers with an intuitive and efficient means to design, deploy, and monitor the entire process. By embracing low-code as a command handler, developers can leverage its agility to streamline the execution of imperative actions, unlocking a new level of efficiency in the CQRS paradigm.

Scenario

As a user, the task at hand is to upload product data from a CSV file into an Airtable.

Flow

  1. User Initiates Upload:

    • The user triggers an upload action by making an API call to the backend, providing a CSV file containing product data.
  2. Controller Dispatches Command:

    • The controller prepares a 'ImportCsvCommand' command encapsulating the user's request.
    • The command is dispatched into the command bus.
  3. Command Handling:

    • The command handler takes charge and performs the following operations:
      • Uploads the CSV file to an S3 bucket.
      • Invokes a low-code workflow through a webhook.
  4. Low-Code Workflow:

    • The low-code workflow follows a structured sequence:
      • Reads the CSV file from the S3 bucket.
      • Parses the CSV data.
      • Inserts the parsed data into the 'products' table in Airtable.

Demo Application

Our application is crafted with NestJS, following the principles of Domain-Driven Design (DDD) and the CQRS pattern. Using NestJs's power, we seamlessly combine these approaches for efficient handling of tasks. This mix ensures our application is not just modern but also well-organized, showcasing the synergy between user-friendly frameworks and smart design principles.

src/app.ts

export default async function bootstrap(): Promise<FastifyInstance> {
  const serverOptions = {
    logger: true,
  };
  const instance = fastify(serverOptions);

  const app = await NestFactory.create<NestFastifyApplication>(
    AppModule,
    new FastifyAdapter(instance),
  );

  app.useGlobalPipes(new ValidationPipe());
  app.enableCors();
  await app.register(FastifyMultipart);

  await app.init();

  return instance;
}
Enter fullscreen mode Exit fullscreen mode

Product Controller

src/product/presentation/http/product.controller.ts

@Controller('product')
export class ProductController {
  constructor(private readonly commandBus: CommandBus) {}

  @Post('/importCsv')
  @HttpCode(200)
  async uploadFile(
    @Req() req: FastifyRequest,
    @Res() res: FastifyReply<never>,
  ): Promise<void> {
    if (!req.isMultipart()) {
      res.send(new BadRequestException());
      return;
    }

    const csvData = await req.file();

    let now = new Date();
    now = new Date(now.getTime() - now.getTimezoneOffset() * 60 * 1000);
    const storageKey = `${now.toISOString()}-${csvData.filename}`;

    await this.commandBus.execute(new ImportCsvCommand(csvData, storageKey));

    res.send();
  }
}
Enter fullscreen mode Exit fullscreen mode

ImportCsv Command & Command Handler

src/product/application/command/import-csv-command.ts

export class ImportCsvCommand {
  constructor(
    public readonly data: MultipartFile,
    public readonly storageKey: string,
  ) {}
}
Enter fullscreen mode Exit fullscreen mode

src/product/application/command-handler/import-csv-handler.ts

@CommandHandler(ImportCsvCommand)
export class ImportCsvHandler implements ICommandHandler<ImportCsvCommand> {
  constructor(
    @Inject(IStorageService) private readonly storageService: IStorageService,
    private readonly httpService: HttpService,
    private readonly configService: ConfigService,
  ) {}

  async execute({ data, storageKey }: ImportCsvCommand) {
    await this.storageService.uploadFile(data, storageKey);
    await this.callLowCodePipeline(storageKey);
  }

  private async callLowCodePipeline(storageKey: string) {
    // invoke webhook
  }
}
Enter fullscreen mode Exit fullscreen mode

Low-Code Flow

For our low-code workflow, we harnessed the power of the Make platform. This user-friendly tool allows us to design our flow through an intuitive drag-and-drop interface. By seamlessly connecting pre-configured connectors, such as the S3 bucket and AirTable table, we effortlessly orchestrate the entire process. 'Make' empowers us to visually design and configure the workflow, ensuring a smooth integration between the S3 bucket and the AirTable table, all with a few clicks and without the need for extensive coding.

Image description

Webhook: the flow entry point

At the start of our flow is the webhook block, acting as the starting point for our process. This essential component kicks off the low-code workflow, setting the stage for the following actions.

Image description

Image description

src/product/application/command-handler/import-csv-handler.ts

@CommandHandler(ImportCsvCommand)
export class ImportCsvHandler implements ICommandHandler<ImportCsvCommand> {
  constructor(
    @Inject(IStorageService) private readonly storageService: IStorageService,
    private readonly httpService: HttpService,
    private readonly configService: ConfigService,
  ) {}

  async execute({ data, storageKey }: ImportCsvCommand) {
    await this.storageService.uploadFile(data, storageKey);
    await this.callLowCodePipeline(storageKey);
  }

  private async callLowCodePipeline(storageKey: string) {
    const result = this.httpService
      .post(this.configService.get<string>('FLOW_IMPORT_PRODUCTS_WEBHOOK'), {
        bucketName: this.configService.get<string>('PRODUCTS_BUCKET_NAME'),
        bucketKey: storageKey,
      })
      .pipe(tap((res) => console.log(res)))
      .pipe(
        catchError(() => {
          throw new ForbiddenException('API not available');
        }),
      );

    await lastValueFrom(result);
  }
}
Enter fullscreen mode Exit fullscreen mode
curl --location 'http://localhost:3000/product/importCsv' \
--form 'csv=@"./sample-data/product.csv"'
Enter fullscreen mode Exit fullscreen mode

sample-data/product.csv

1;First Product;10.50
2;Second Product;20.00
3;Third Product;30.00
Enter fullscreen mode Exit fullscreen mode

After the execution:

Image description

From S3 Reading to CSV Parsing

Moving ahead, we shift from reading data in the S3 bucket to the CSV parsing phase. The low-code system smoothly collects the CSV file and then effortlessly processes it through parsing.

Image description

Image description

Final Step: insert into AirTable

As the final step, our low-code workflow seamlessly inserts the parsed data into the designated AirTable.

Image description

Image description

Image description

Conclusions

The project code is in this GitHub repository: cqrs-with-low-code.
Embracing a low-code approach for certain command handling in our application has proven advantageous, notably in expediting feedback loops. Delegating tasks to the low-code pipeline streamlines command execution, fostering a quicker response mechanism. This efficiency not only enhances our development process but also empowers us to adapt and iterate swiftly, ultimately contributing to a more responsive and agile system.

cqrs Article's
30 articles in total
Favicon
CQRS — Command Query Responsibility Segregation — A Java, Spring, SpringBoot, and Axon Example
Favicon
An opinionated guide to Event Sourcing in Typescript. Kickoff
Favicon
The best way of implementing Domain-driven design, Clean Architecture, and CQRS
Favicon
Understanding the CQRS Pattern
Favicon
Missive.js
Favicon
Event-Driven Architecture, Event Sourcing, and CQRS: How They Work Together
Favicon
Mediator and CQRS with MediatR
Favicon
Implementing CQRS and Event Sourcing in .NET Core 8
Favicon
CQRS (Command Query Responsibility Segregation)
Favicon
Implementing CQRS and Event Sourcing in Distributed Systems
Favicon
CQRS and Mediator pattern
Favicon
Vertical Slice: Um Déjà Vu do CQRS
Favicon
Demystifying CQRS for Junior Developers: A Friendly Guide
Favicon
CQRS with Low-Code
Favicon
One of many ways to migrate from NodeJS to Rust
Favicon
Understanding CQRS Pattern: Pros, Cons, and a Spring Boot Example
Favicon
Integrating CQRS and Mediator in .NET with MediatR: An Elegant Convergence for Robust Applications
Favicon
Implementing CQRS in ASP.NET using MediatR
Favicon
CQRS Pattern With MediatR
Favicon
What is CQRS Pattern?
Favicon
Why do not you use IPipelineBehavior ?!
Favicon
Implementing the CQRS Pattern in NestJS with a Note API Example
Favicon
CQRS+
Favicon
CQRS and Event Sourcing for Software Architecture.
Favicon
Getting Started with Event Sourcing and EventSourcing.Backbone
Favicon
CQRS Pattern in Microservices Architecture
Favicon
Implementing CQRS in Python
Favicon
Easiest way to build the fastest REST API in C# and .NET 7 using CQRS
Favicon
Idempotent Consumer - Handling Duplicate Messages
Favicon
Notes on Microservices — Part 1

Featured ones: