npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@leivaa/simple-cqrs

v1.0.9

Published

A simple lightweight CQRS library inspired in the nestjs/cqrs module

Downloads

4

Readme



About

simple-cqrs is a package that aims to bring CQRS to its simplest. Its written using Typescript, so it already brings all the d.types.

The package aims to give a smooth programming experience, and its inspired in frameworks like angular and nestjs.

  • Bases

    Command Query Responsability Segregation is a design pattern that aims to keep the infrastructure handler and the service handler decoupled.

Getting Started

All the code for this example is public on the leivaa21/simple-cqrs-express-example repository.

  • Prerequisites

    • Made using:

      • Node v18.3.0
      • NPM v8.5.5
      • Typescript v4.7.3

      so these are the recomended versions.

    • tsconfig.json has to have the property

        "experimentalDecorators": true,

      for being able to use decorators (needed in this package)

    • for installing this package in your npm project, use: npm install @leivaa/simple-cqrs

  • How to use

    • Queries

      Query and Handler structure

      Keep in mind that Queries are meant to return information from our application BUT are not meant to modify the status quo of our application itself.

      getUser.query.ts

      export default class GetUserQuery {
        constructor(public readonly id: string) {}
      }

      getUser.handler.ts

      const config: QueryHandlerConfig = {
        Query: GetUserQuery,
        Dependencies: [InMemoryUserRepository],
      };
      
      @QueryHandler(config)
      export default class GetUserQueryHandler
        implements IQueryHandler<GetUserQuery, GetUserResponse>
      {
        constructor(private readonly repository: UserRepository) {}
        async handle(query: GetUserQuery): Promise<GetUserResponse> {
          //Your code
        }
      }
      • Its recomendend to use abstractions in the constructor, but "Dependencies" in the handler config should always have a implementation class!
      • The implementations given in "Dependencies" should always be marked as "Injectable" (More info down below!)

      Using the query bus

      Here you have an example of controller following the same example as the one given above. This example uses Express, but the framework doesnt matter, and it would work in vanilla node too ie.

      getUser.controller.ts

      export default async function getUserController(
        req: Request,
        res: Response
      ) {
        //Some code here
        const QueryResponse = await QueryBus.dispatch<
          GetUserQuery,
          GetUserResponse
        >(new GetUserQuery(id));
        //Some code more
      }
    • Commands

      Command and Handler structure

      This is pretty much the same as in the query side, only changing Query for Command!

      Keep in mind that Commands are meant to modify the status quo of our application BUT are not meant to return nothing.

      createUser.command.ts

      export default class CreateUserCommand {
        constructor(public readonly id: string, public readonly name: string) {}
      }

      createUser.handler.ts

      const options: CommandHandlerConfig = {
        Command: CreateUserCommand,
        Dependencies: [InMemoryUserRepository, UserFactory],
      };
      
      @CommandHandler(options)
      export default class CreateUserCommandHandler
        implements ICommandHandler<CreateUserCommand>
      {
        constructor(
          private readonly repository: UserRepository,
          private readonly factory: UserFactory
        ) {}
        async handle(command: CreateUserCommand): Promise<void> {
          //Our code here
        }
      }

      Its important to keep in mind that "Dependencies" in the handler config should always keep the same order as in the constructor.

      Using the query bus

      getUser.controller.ts

      export default async function createUserController(
        req: Request,
        res: Response
      ) {
        //Some code here
        await CommandBus.dispatch<CreateUserCommand, void>(
          new CreateUserCommand(id, name)
        );
        //Some more code here
      }

      CommandBus should always use the return type void as handlers are meant to not return nothing.

    • Injectables

      Injecting dependencies brings a lot of decoupling to our apps, as long as we only depends on abstractions and not implementations of our services.

      Keeping with the same example, this would be how we create an Injectable:

      UserFactory.ts

      const config: InjectableConfig = {
        Dependencies: [],
      };
      
      @Injectable(config)
      export default class UserFactory {
        create(id: string, name: string): User {
          //your code
        }
      }

      Dependencies in the config works just like dependencies in hanlders. So its possible to inject dependencies to injectable objects.

    • CQRS Module

      Its important to know that the cqrs module has to be initialized in some way, and all the Handlers & Injectables have to get loaded. So its important to set up the CQRS Module in a space of code that will be loaded at the same time as our application start.

      cqrs.module.ts

      const config: CQRSModuleConfig = {
        QueryHandlers: [...QueryHandlers],
        CommandHandlers: [...CommandHandlers],
        Injectables: [InMemoryUserRepository, UserFactory],
      };
      
      CQRSModule(config);

      Using the spread operator its a way of keeping the module config cleaner, but this will make us made a index.ts with all the handlers loaded into an array, like this:

      application/index.ts

      export const CommandHandlers = [CreateUserCommandHandler];
      export const QueryHandlers = [GetUserQueryHandler, GetAllUsersQueryHandler];

      Some times injectables are loaded by other handlers and it wont fail even if you dont load them into the CQRS Module, BUT it is a good practice to load them just in case it doesnt load all the injectables.

License

This project is under a MIT license, so its open source and open for contributions :). More info about the license here