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

@timreynolds/cqs

v1.0.3

Published

TypeScript Command Query Separation Types

Downloads

8

Readme

TypeScript Command Query Separation Types

Build Status Coverage Status

Typescript declarations for the Command query separation pattern.

CQS promotes the separation of methods that return and mutate state, put simply;

  • Queries: Return a result and do not change the observable state of the system (are free of side effects).
  • Commands: Change the state of a system but do not return a value

The main advantage of this pattern is the confidence it provides consumers on the effect each method has on your application state.

In practice most well structure services within the domain layer of a three tier application can easily be split into commands a queries. When doing so you'll the pattern can also provide the following benefits;

  • Functions with explicate contracts are easy to decorate with cross cutting concerns
  • Transaction or unit of work approaches can be applied only to command handlers
  • Only commands must confirm to domain model, queries can be optimized for consumers

Handlers

Regardless of it being the command or query channel semantically there are two main concepts.

  • Handler, function that receives context to mutate or retrieve data
  • Invoking context object, will be either a query or command

Below is a simple example to demonstrate the concepts.

Command

const createStaffCommand = {
  firstName: 'Tim',
  lastName: 'Reynolds',
  role: 'Developer',
  email: '[email protected]'
}

Command Handler

const createStaffCommandHandler = async (command) => {
  validate(command);
  const newStaffMember = new Staff(command);
  await repository.create(newStaffMember);
  await publisher.publish(newStaffMember.events);
}

Examples

Building Handlers

Most handlers, command or query, will have dependencies e.g. repository. Injecting dependencies can easily be achieved while maintained the signature of the command by capturing them in a closure.

module.exports = connection => {
  const staffRepository = StaffRepository({connection});
  return {
    findAllStaffQueryHandler: findAllStaffQueryHandler({ staffRepository })
  };
};

This can be applied to one or many commands and abstracted out into builder functions based on the dependencies of the handler.

Unit of work decoration for transactions safety including event publishing

When commands emit events or mutate data in more than one source you'll often want to ensure consistency via a transactional boundary. This is often a cross cutting concern and can be performed via closures.

// Take the dependencies then curry to return
// a function that has the same signature as the command
const unitOfWorkHandlerBuilder = (handler, connection, publisher) => command => {
  // When the handler is called, we'll then create a transaction
  // Mock publisher to store events until transaction is successful
  const unitOfWorkPublisher = unitOfWorkPublisherStubFactory();
  return connection.tx(txn => {
      // Recreate the dependencies that are transaction dependent
      const staffRepository = staffRepository({connection: txn});
      // Execute handler with dependencies and command
      return handler({staffRepository, publisher: unitOfWorkPublisher})(command);
    })
    // Move the events published inside the handler from mock to real publisher
    .tap(() => publisher.publish(unitOfWorkPublisher.getEventQueue()));
};

This approach leads to a complex builder but removes the transactional cross cutting concern from each handler. Other cross cutting concerns that can be tackled with this approach including logging query handler query and result.

FAQ

You mean CQRS? Isn't this CQRS?

Nope, Command Query Responsibility Segregation is a similar but different approach often accredited to Greg Young. Although the premise of each are similar the use of CQRS often promotes the decentralisation of the query model often in separate data stores designed to meet the needs of the query model often via messaging and eventual consistency.

But I need my command handler to return data?

Often returning data from be avoided for two reasons.

  • Firstly, mutated state is already held by the client or upstream so resolved/rejected is enough
  • Secondly, if ids are required by the client or upstream for later operation they can create upstream when providing the new state.

There are still edge cases where this might not hold true so don't try to force it, there is an implementation where the command holds a Result property that can be called after command handler execution.

Should I use CQS all the time?

Often, but not always. The overhead of using this pattern is low but works best with the domain model approach in a system when effort in modeling entities has been performed using DDD or similar.

Personally I promote these techniques in larger complex data heavy applications. Smaller micro-services or systems that don't deal with the persistence of data won't see the benefit.

Where can I learn more?

Information about CQS is less discoverable than other software patterns. Martin Fowler provides a good overview on his blog