Investigating the CQRS pattern using GraphQL and SPQR

I was speaking with a colleague recently and he mentioned the acronym CQRS. I was not familiar with this acronym, so I decided to do a little research. It’s a design pattern and it stands for Command Query Responsibility Segregation. The general idea is that it’s a “good thing” to separate out the reading of data from your system from the things that affect that same data. Hence, “Command” “Query” “Segregation”. It’s a pretty simple concept on the surface, but the decoupling can be very powerful, since you no longer have to worry about your front ends keeping the data model of the two aligned. Martin Fowler has a very nice write-up here https://martinfowler.com/bliki/CQRS.html, so I won’t go any further myself.

I’ve been doing some GraphQL based architectures recently, and it dawned on me that GraphQL and the CQRS design pattern are very compatible. If you’re not familiar with GraphQL, it was invented by Facebook and open sourced in 2015. I’ve been using it recently because it is very bandwidth and latency efficient. Rather than a REST service where you may need to make numerous round trips to fill in your User Interface, GraphQL allows you to specify the exact data you need, including related data, all at one time. It looks something like this:

query {
Member(id:200) {
firstName
lastName
}
}

Simple, right? GraphQL doesn’t require the “query” keyword (it’s implied), but I like to use it for readability and compatibility with mutations (more on those in a minute). Now, the above example doesn’t really do GraphQL justice, because you could just as easily perform this with a REST request, but what about this?

query {
customer(id:"ANDER") {
firstName
lastName
orders(first:10) {
orderDate
deliveryDate
deliverySignature
}
}
}

The advantages over REST become more apparent.

To make changes using GraphQL, you use something called a mutation. According to the spec, any time the target system changes any data, it should be handled as a mutation. A good example is a user logging in, since the target system should at least record the fact that a user has logged in, if not additional data like storing a generated token or hash. To GraphQL, a mutation is completely separate from queries, hence my thought that GraphQL and CQRS are very compatible. For example, when a user logs in, you might have a mutation that looks like this:

mutation {
emailLogin(emailAddress:"chad@gmail.com",password:"password") {
memberId
sessionToken
}
}

The values passed to the mutation (emailAddress & password) are unrelated to values stored in the member table, and could easily be different. In addition, you could easily add parameters to the mutation that affected different entities in your data model.

There are numerous valuable resources on the web describing GraphQL, including a significant number of additional tools and implementations. I suggest you use the search engine of your choice to find out more.

So, with thoughts of CQRS running through my head, I decided to put together an example to show the power of the separation, as well as exercising some of the other tools I’ve been working with recently.

Our example today is a back end service that implements a way to store user preferences for ranked items. As an example, we’ll allow users to rank their favorite types of cuisine! As a side benefit, we will also demonstrate a few hoops you have to jump through when you actually have the SAME data model backing both the queries and mutations (or commands), and we’ll discuss how it might be better to separate them even in this simple example.

The tools we’ll use are:

If you’re already using Java & Gradle, you’ll probably only have to download Cayenne (to run the modeler) and GraphiQL (to connect to the server). Everything else should be downloaded by Gradle automatically for you.

Personally, I use STS (Spring Tool Suite) as an IDE, but you can build this project simply with gradle command line tools.

You can clone my repository from here to get started: https://github.com/anderkh/prefs-graphql

Lets start building!

The server application is quite simple. The main application class (PrefsApplication) defines 3 beans — the cayenne service (to link Cayenne into the Spring environment) and 2 entity services — MemberService and ProfileQuestionService. The database backing these beans is straightforward from a normalized relational perspective:

  • Member stores the users that create accounts in the system.
  • ProfileQuestion and ProfileQuestionOption stores the questions and options.
  • MemberProfileQuestion and MemberProfileQuestionOption store the relationship from the members to how they answered the questions.

Let’s get the server running, so that we can walk through some examples and look at how it’s implemented.

Use the Cayenne Modeler to open cayenne-project.xml in the src/main/resources folder. Feel free to take a look around and see how Cayenne lays out the tables and entities.

Go to the Cayenne Modeler preferences, choose ClassPath, and add a jar/zip to the driver for your chosen database.

Next, create a database and use a user that has full rights to create tables, etc. If you’re using MySQL and you’re unfamiliar, connect to the server and type:

create database PrefsLocal;

Then, choose Local DataSources in the Cayenne Modeler, and add your database. When you test the connection, if you get an error about an invalid timezone, add ?serverTimezone=America/New_York to the end of your DB URL string, like this:

jdbc:mysql://localhost/PrefsLocal?serverTimezone=America/New_York

After testing your DB connection, go to the Tools menu and select “Generate Database Schema”. A dialog will appear that will allow you to create the tables in your database. Click on “Generate” and choose the data source you just created. If for some reason you can’t get Cayenne Modeler to connect to your database, you can always copy/paste the creation commands from the dialog box into your database front end or terminal.

Once your database is created, put some data into the model so we have a question to answer!

INSERT INTO `profile_question` VALUES ('What is your favorite kinds of food to eat?',1,true,1,'Favorite Cuisine');
INSERT INTO `profile_question_option` VALUES (1,1,'Italian'),(2,1,'Greek'),(3,1,'Chinese'),(4,1,'American'),(5,1,'BBQ');
commit;

These statements are compatible with MySQL, but the data is simple, and should work easily with your database of choice.

Lastly, modify the application.properties file in the src/main/resources folder to match the port, driver, username, password, and url for your database.

Then, either use your IDE to refresh the Gradle project and build/run, or let Gradle do all the heavy lifting for you:

In Linux or MacOS, make sure you’re in the prefs-graphql folder:

./gradlew build
java -jar build/libs/Prefs-0.0.1-SNAPSHOT.jar

In Windows, make sure you’re in the prefs-graphql folder:

gradlew build
java -jar build/libs/Prefs-0.0.1-SNAPSHOT.jar

Hopefully your server is now up and running! Let’s get down to business!

First, let’s create a user. Launch GraphiQL and point it at the server you just started. If you haven’t changed the port in application.properties, it should be at:

http://localhost:8989/graphql

If you’re unfamiliar with GraphiQL or GraphQL in general, take a look at the Documentation Explorer on the right (click on “Docs” if it’s not open). You’ll see you can inspect both the query and mutation capabilities of this schema.

Typical GraphQL servers have a schema file that defines the schema and describes functions for accessing and mutating data. For this example, we’re using a library called SPQR that auto-generates the schema for you based on annotations. We’ll get into how that works as we go along.

NOTE: You might have noticed that the URL we’re using is http, not https. Since we’re just working locally on our machine, that’s OK — we don’t want to deal with certificates at this point. Rest assured, in a production environment, we’d install an SSL certificate on the server.

Let’s create a member!

Type the following into the left pane in the GraphiQL user interface:

mutation {
createEmailMember(firstName:"Test", lastName:"User",
emailAddress:"test@user.com", password:"password") {
memberId
sessionToken
}
}

I suggest you type it in, rather than copy/paste, so you can see how the GraphiQL UI (with the help of the schema) walks you through the inputs.

Let’s take a look at MemberService.java to see what’s going on here. At around line 70 you’ll see that a method has been defined called createEmailMember:

@GraphQLMutation
public Member createEmailMember(
@GraphQLArgument(name="firstName") String firstName,
@GraphQLArgument(name="lastName") String lastName,
@GraphQLArgument(name="emailAddress") String emailAddress,
@GraphQLArgument(name="password") String password)
throws DuplicateEmailException {
...
}

The method itself is annotated with @GraphQLMutation, and the arguments we want exposed in the GraphQL schema are annotated with @GraphQLArgument. With these annotations, SPQR has built the schema automatically for us. As you can see, the method first checks to see if a user exists with that email address. If it does, you’ll receive an error back to the GraphiQL front end. Otherwise, a record is built, and we use the bcrypt library to store a password hash. Lastly, we generate a session token for the user, save only the hash code to the table, and save to the database. You’ll likely want additional error handling here for potential database issues, but I’ve left that out for the sake of this example.

Now take a quick peek at Member.java. You’ll notice that sessionToken is actually an instance variable of this class, and is not stored in the database. You’ll also notice that we have added the @GraphQLQuery annotation to the getter. This allows us to return the session token when we return the Member object in the createEmailMember method, but it will disappear when this Member instance is garbage collected. This is a nice way with GraphQL and SPQR to return attributes that are ephemeral in nature.

If everything worked OK, you should see a return that looks something like this:

{
"data": {
"createEmailMember": {
"memberId":<ID>,
"sessionToken":<TOKEN>
}
}
}

Make note of the memberId and sessionToken — we’ll have to use them for the rest of the exercise (or you could log in again, or create a new user).

To retrieve the user, you can just use the member query, like so (substituting the correct member ID and session token):

query {
member(id:<ID>, sessionToken:<TOKEN>) {
firstName
lastName
}
}

Experiment with returning different attributes of the member, as well as with passing in invalid IDs or tokens.

We started this whole endeavor to talk about CQRS. As you can see, there’s a pretty distinct separation between queries and commands in the way GraphQL works. Technically, you could even have 2 endpoints, using one endpoint only for queries, and one only for commands.

Now that you have your server up and running and a user defined, let’s delve into the complexities of using a single model to handle both your commands and queries (like we’re doing here). Thankfully, the hoops we need to jump through are eased by the features of SPQR, which will help us built intermediate objects.

Run this query in GraphiQL:

query {
profileQuestion {
profileQuestionId
title
profileQuestionOptions {
profileQuestionOptionId
questionOption
}
}
}

Simple, right? A nice thing about GraphQL is the fact that you can layer your queries and get responses that have all the data you need without multiple round trips. This query can be used by a user interface to get the profile questions to be filled in by the user. BUT, what if this user has already filled in answers? Do we want to make round trips and write a lot of code to merge everything? Certainly not! We want the front end developer to have a very simple way to get the questions, answers, and how our current user answered them. Try this:

query {
member(id:<ID>, sessionToken:<TOKEN>) {
profileQuestions {
title
descr
profileQuestionId
profileQuestionOptions {
profileQuestionOptionId
questionOption
ranking
}
}
}
}

Cool, right? We have morphed our data model to return the profile question(s), even though the member hasn’t answered them yet. This fits MUCH better with the way a user interface would display it, lending credence to the idea that your query model can (and often should) look different from your command model.

Let’s take a look at how it was done. Take a look in MemberService.java again. There’s a method called profileQuestions, that takes a Member instance as an argument that is annotated with @GraphQLContext. This magic annotation says to add “profileQuestions” as an attribute accessible from the Member object in GraphQL space. If you notice, there’s no “profileQuestions” relationship or method on the Member object that would allow you to satisfy the above query. The profileQuestions method returns a list of Java instances of class QuestionForMember. It is our ad-hoc join between the member, and the questions.

If you take a look at the profileQuestions method, you’ll see that we iterate over the active questions in the system, then use the new forEach stream method on List, plus an inline lambda function, to build our QuestionForMember list. These new features of Java 1.8 are very welcome, helping us get closer to a more functional programming model:

List<ProfileQuestion> questions =
ObjectSelect.query(ProfileQuestion.class)
.where(ProfileQuestion.IS_ACTIVE.eq(true)).select(oc);
   questions.forEach(pq -> {
QuestionForMember qfm = new QuestionForMember();
qfm.setProfileQuestion(pq);
qfm.setMemberProfileQuestion(
member.memberProfileQuestionForProfileQuestion(pq));
   qfms.add(qfm);
});

If you now take a look at the QuestionForMember class, you’ll notice we’re leveraging the new Optional generics class to link the ProfileQuestion to the (possibly non existent) MemberProfileQuestion. The method profileQuestionOptions() in this class then links the answers in a similar fashion (with the AnswerForMember class), again leveraging streams and lambdas to join and sort our answers.

The last piece of our puzzle is the mutation setRankingForOrderedProfileQuestion in MemberService.java. This method, through SPQR, adds a mutation to our GraphQL schema that allows the front end to set the ranking of our answers for each question. Try this:

mutation {
setRankingForOrderedProfileQuestion(memberId:<ID>,
sessionToken:<TOKEN>,
profileQuestionId:1,
profileQuestionOptionIds:[5,4,3,2,1]) {
profileQuestions {
title
descr
profileQuestionId
profileQuestionOptions {
profileQuestionOptionId
questionOption
ranking
}
}
}
}

This is the mutation (command) that sets the preference order for the first question, using the option IDs in the order of preference.

You have now reordered your cuisine preferences (I love BBQ), and received the completed response, all in one shot.

Of course, we jumped through a lot of hoops to make our query match how we should best consume the data. What would be another way to approach this? How could you further leverage the CQRS pattern to make this easier?

What if the mutations placed the correct permutations on a message bus to another process, or maybe even generated the completed JSON for each question and placed it in a NoSQL database? Stay tuned!

Like what you read? Give Ken Anderson a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.