Project

cqs

0.0
The project is in a healthy, maintained state
cqs allows you to separate queries from commands for a better segregation of concerns.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Runtime

>= 6.1, < 9
 Project Readme

CQS - Command Query Separation for Ruby (and Rails)

Gem Version

⚠️ This gem (and readme) are still Work In Progress ⚠️

Unlike CQRS, this gem tries to give a framework that allows to separate Commands from Queries in your Ruby programs.

Working on a Ruby on Rails application we got tired and frustrated about the ApplicationService pattern that is so prevalent in so many Rails codebases.

Here are a few reasons we did and did not like the pattern:

  • A Service can be almost anything
  • We are never sure what the service really does and we end up, if not careful, creating similar services
  • The language got a little muddled

For these reasons we opted for creating a simpler language in our application.

When requesting information use a Query

When requesting a change use a Command

One thing we did not want to implement (the reason this is not a CQRS implementation) is the strictness of a pure CQRS implementation (meaning commands can, and many times will, have return values, specially if you consider a #save action in a Rails app).

Usage

Most of the times we use this gem in a Ruby on Rails project, so the usage examples will be for Rails. If you use it in plain Ruby projects it will still work, you just won't have access to the generators.

We are opinionated with the use of RSpec to test drive our code. Let us know if you'd like to have other testing tools supported!

General assumptions

Commands and Queries operate on a subject. The subject is passed into the action by the provided methods (wtih for commands, and in for queries):

FindUser.in(email_address: "test@test.com")

You can register methods to change the way you call your action:

class FindUser
  include Query
  register_method :by
  ...

And then call the action with the method registered:

FindUser.by(email_address: "test@test.com")

This works for both, Commands and Queries.

The subject is what's passed into the calling method and can be anything you like (see below to see how it's used).

Queries

Imagine you want to create a query to find a user by passing a hash with some of their attributes (find_by in Rails).

To save time and setup you'll run the provided generator:

rails generate cqs:query FindUser

This will create the following files:

- app/queries/query/find_user.rb
- spec/queries/query/find_user_spec.rb

The generated Query looks like so:

module Query
  class FindUser
    include Query

    def answer
      raise AnswerUnknownApology.new "Please implement #answer"
    end
  end
end

And the corresponding spec:

require "rails_helper"

describe Query::FindUser do
  it "does something" do
    pending "test your query"
  end
end

First we'd write our spec to ensure our Query does what we'd like it to do:

require "rails_helper"

describe Query::FindUser do
  it "finds a user by their email address" do
    email_address = { email_address: "test@test.com"}
    expect(User).to receive(:find_by).with(email_address)

    Query::FindUser.by(email_address)
  end
end

and the code to make this pass:

module Query
  class FindUser
    include Query
    register_method :by

    def answer
      User.find_by(subject)
    end
  end
end

Development

After checking out the repo, run bin/setup to install dependencies. Then, run bundle execute rspec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/OurWeeSaas/cqs. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the CQS project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.