My first event-sourced aggregate in Ruby…

Having read the last three posts, you could have an impression that we’re never going to start developing. Brace yourself because today we’re gonna do it! Today, we will take our keyboards and move the models we’ve proposed to the code. I like to define models we’ve created during DLES as “propositions”. That’s because whenever we sit down and start the implementation, things can change. That’s normal, and we call this process a modeling whirlpool. We will keep on asking experts about some details. Knowledge crunching is an ongoing process, and our models will evolve and adjust to new business requirements. Ok, so without further ado, today we will implement our first test for our first aggregate, and we will set up the whole stack with CQRS, Event Sourcing, and… Rails. Yup, you’ve heard that right, me, a Java fanboy, said that 🙂

Note: all the code for this and future posts is available here.

Ruby? You’re kidding me, aren’t you?

After consideration, I came up with the conclusion that it will be the best for everyone. First of all, I will tell you what I get out of it. So, for a long time, Ruby has been on my TO-DO list, and now it is a perfect moment to learn it. We, developers, improve our skills when we try other languages because it shows us different perspectives. I used to work primarily with strongly typed languages, so jumping into weakly typed ones can be a pretty exciting experience.

Ok, that’s about me, now let’s switch to “objective” arguments  – or rationalization, you name it 😉

The initial idea was to implement the whole infrastructure for the event-driven architecture (when you get back to the first post, you can see that was the plan). By “infrastructure,” I mean, for example, command and event busses themselves and their integrations. Also, we would need to have a post where we analyze different solutions and the reasons for choosing the specific ones. Moreover, we would need to do it somewhere between Design Level Event Storming and the first implementation of the aggregate. From a technical point of view, it would be a fantastic task. I’m pretty sure we would have a lot of fun with it, but at the same time, it would keep us away from the most crucial thing – moving the model to the code. So we would have a long interlude, and we could lose our zeal for work. Nevertheless, all is not lost. Now I will focus on the model implementation. Sometimes, if I’m tempted to show you how things work in Java, I might write a post about it 😉

Regarding Event Sourcing, we would probably skip this fascinating concept and use a standard persistence mechanism. I also want to mention that currently there is no “native” Java solution that is an obvious choice for an event store. That’s why we would need to do some research and consider, for example, Event Store with its Java SDK (gRPC). Or maybe, we could drift toward Akka and the actor model. We could spend hours thinking about all those exciting tools, but I think this is not the right time.

Fortunately, while talking with Andrzej Krzywda about their (Arkency) Rails Event Store (RES), he asked if I would like to use it in this project. Of course, my first thought was: “no way – Java for life!” but then I realized that it actually makes sense. The outcome of Adrzej’s indoctrination was that the next day, I texted him about using RES in my project 🙂

Below, a couple of arguments for going with RES in this case:

  • Arkency has used DDD in projects for years. They share knowledge by publishing posts, doing presentations, courses, and workshops, and they’ve also written an excellent book about DDD: “Domain Driven Rails”. Don’t let the title fool you – whether you are a Ruby developer or not, you should read it.
  • They develop a tech stack (RES is a piece of it) and techniques that have worked best for prod environments during all those years. In other words, their approach is mature and battle-hardened. It’s also consistent with how I see DDD.
  • RES provides us with the whole infrastructure for commands, events, and event sourcing. We get it out of the box, then we can focus 100% on  the most important thing – the model. That was the main reason why I decided to use it.
  • Last but not least: Andrzej promised that whenever I get stuck somewhere (remember – I’m still a Ruby noob), I can get some support or review from them 😉

Now I’m pretty sure you are as excited as I am!

Let’s do it the “all-in” way!

By all-in I mean the approach where DDD, CQRS, and Event Sourcing are combined together. However, I’m aware that this may not be an option for most already running projects without revolutionizing the codebase. Therefore, if I want to show you a more evolutionary way (applicable to most projects), I would probably skip Event Sourcing in the early phase and go with some standard persistence mechanism. The distinction between write and read models isn’t so obvious too. I would probably start with something like “snapshots” from aggregates during the read operation. But the idea for this series of posts is different – I would like us to learn and practice concepts that usually are presented only in theory. When combined, those concepts can be a massive game-changer for every project.

One last thing before we get to the keyboard: I need to explain how I understand those three concepts, so we all are on the same page.

Even in the previous post, you could’ve seen the “aggregate” word in use. I assumed it’s pretty common in IT, so I skipped the explanation, but I would like to write a few sentences now.

Well, in aggregate, we… aggregate. So we collect data/objects that are grouped together as a logical unit. We protect its consistency. In practice, it means that the aggregate is the only place where this data can change. So we want to avoid situations where different pieces of code change them. That’s why an aggregate is also a place where we keep all invariants that help us keep everything consistent. Doesn’t it ring a bell? Consistency, data, and behaviors together. What was that? Encapsulation? OOP? 😉

I also mentioned CQRS and Event Sourcing so let’s explain those two quickly.

CQRS (Command Query Responsibility Segregation) has its roots in Command Query Separation (CQS) proposed by Bertrand Meyer. It was even considered CQS at a higher level for some time. CQS itself says that a method of an object can do one of the two things: perform an action (change the object’s state) or return data to the caller, and never both of them. However, Greg Young took this principle a stage further and proposed to use it at a higher level. As a result, we have two separate models: the write and the read models. From the architectural point of view, it results in exciting consequences. On the one hand, we have pretty heavy write operations, where we keep complex business rules and protect data consistency. On the other hand, we keep lightweight read models – for example, DTOs created directly from specialized SQL queries results. 

We have 90% of reading operations in most IT systems, and only 10% write operations. So it means that if we have a dedicated read model, the whole system will be more performant than the same system, but with a single mixed model used for both: writes and reads. At the end of this post, you will see an example of the write and read model segregation in action. Stay tuned!

If you want to know about CQRS itself, I encourage you to read this document by Greg Young – the source of CQRS.

Last but not least: Event Sourcing. It’s nothing else than one of the objects’ persistence techniques. We keep only the newest version of the object’s state in the standard approaches you probably know from most projects. Instead, in Event Sourcing, we store all statechange events. Then, creating the object with its current state is done by “replaying” all those events. Thanks to that, we can also recreate any state from an object’s history. 

We store events in what’s called Event Store, and Arkency’s RES is an implementation of such a store.

I could write separate posts about those concepts, but what we have here should be fine now. It’s important to mention that those techniques can be used in the separation. You could even use CQRS in a project where you keep your business logic in services. Event Sourcing is just a persistence technique. None of them is required for the DDD approach. Nevertheless, all three combined together sum up to architecture (event-driven, with cohesive modules), typical for apps with a good design.

Ruby and the RES setup

Initially, I had a few issues with Ruby setup because I installed it as a root user on my Ubuntu. I did the same with Rails. It wasn’t a good idea. Especially now when I tried to install another version of Ruby using rbenv. Things started to collide. Finally, I uninstalled everything and started from scratch using this post. I assume that for other systems, it can be pretty similar:

  • install all required for Ruby dependencies (you may already have them)
  • install rbenv
  • install Ruby 2.7.6 using rbenv
  • install gems: bundler, rails

Now we can create a new RES project. The documentation and the tools that devs in Arkency have created can be helpful. All you need to do is run this command from a CLI:

rails new -m https://railseventstore.org/new YOUR-APP-NAME

It will bootstrap the RES project with all dependencies and default settings.

Then, if you run: 

rails s

and go to http://localhost:3000 in a browser, you should see a standard Rails page. Also, if you visit the http://localhost:3000/res URL, you should see the events’ browser.

It’s a good moment to confess to something… I do not like reading manuals – well, documentation is some kind of a manual ;). Of course, there is everything I should do on a RES page step by step. Why should I use it? That’s not for me. I’ve just managed to install a RubyMine, so I’m in the God Mode now – nothing can stop me.

Moreover, Arkency has created an Open Sourced example that I can spy on. I open it in a separate window on the second screen. I can see some aggregates, so I manually create them and the directory structure. I add something in one place, remove something else in the other – I’m pretty sure that eventually, it will work. Well, it did not. I took a few deep breaths and returned to the “What next?” section. We can see how the setup in the context of Rails looks or how we can change the events’ serialization approaches. But our next step now will be the initialization of a new Bounded Context, which can be done by a generator.

Let’s try it with the advertisements BC:

rails generate rails_event_store:bounded_context advertisements

After running this command from the CLI:

  • structure of directories for a new module was created:

advertisements
├── lib
│   ├── advertisements
│   └── advertisement.rb
└── test
   └── test_helper.rb

  • config for class loading was added to the application.rb file:

config.paths.add ‘advertisements/lib’, eager_load: true

Where to start? Test maybe?

It’s time to write our first test. Let it be the test for advertisements’ content change. Let’s start with finding this piece in our model from Design Level Event Storming:

We can move that straight into the code. First, we should throw the command that should trigger aggregate’s business method execution. Aggregate itself should protect consistency, and after execution, we expect a specific event:

The asser_events method is a helper method that I’ve stolen from Arkency’s sample app. It reads events from the event store and checks whether there are the same as the expected ones:

The test, of course, does not pass, so now we should focus on implementing all the missing things. We can see that we are missing two commands and one event for sure. The first command – PublishAdvertisement – is in the Arrange section because if we would like to change the advertisement’s content, it actually has to be published/created.

The project’s default configuration supports class loading from the advertisements/lib/advertisements directory. Nevertheless, I like to keep commands and events in separate dirs. That’s why I create those classes in such a structure:

advertisements
├── lib
│   ├── advertisements
│   │   ├── commands
│   │   │   ├── change_content.rb
│   │   │   ├── publish_advertisement.rb
│   │   └── events
│   │       └── content_has_changed.rb

We need to explicitly tell Rails where to find them in such a case. We can do it by adding a few lines to the config/initializers/zeitwerk.rb file:

Now we just have to implement all those classes:

  • PublishAdvertisement command:
  • ChangeContent command:
  • ContentHasChanged event:

We see that it’s still red when we run the test because we still miss commands handling and the aggregate that would throw the event. So let’s start with the handling of the commands. We will keep handlers in the advertisements/lib/advertisements.rb file:

Those examples show exactly how, in general, handlers should work. We should load an aggregate there, and then execute the business method.

Now we have to register our handlers on the command bus, and we can do it in the RES config file (config/initializers/rails_event_store.rb):

When we get back to the handlers themselves, we see the two last missing pieces: the aggregate and the repository.

Let’s implement the repository first (advertisements/lib/advertisements/advertisement_repository.rb):

You can notice here that RES via AggregateRoot class gives us an API for the repository. e can use that repository with the name of the events stream then. Theoretically, we could use this repository directly in handlers, but personally, I prefer having dedicated repositories per each aggregate. Mostly because I would like to have a coupling to the “framework” on the level of handlers. Thanks to this, we keep persistence details on the lower level.

By the way, you can also see here how the idea of event sourcing works. First, we pass a new object (Advertisement.new), the stream’s name, and a block/lambda. Then under the hood, events for the given aggregate’s ID are fetched and replayed on this newly created object. Finally, the given block is executed, which usually means the business method of the aggregate.

OK, so now we are almost there. The only missing part and the cherry on top is the aggregate’s implementation:

We have here business methods that protect consistency. Moreover, we throw events here.

What you see below business methods are event handlers in the aggregate context. According to RES’ convention, they start with on and then there is the event’s name. We’ve changed the aggregate’s state in those methods. We’ve done it this way because we want to separate responsibilities of running business rules and the state change. And that’s awesome!

Now let’s try to run the test one more time…

Hurray!!!

I would like to remind you the test and how it looks:

Actually, a single, specific line:

Please notice that we are expecting an event with the new content here. Now recall: do we keep this content in the aggregate itself? No. And that’s actually the most “mind-blowing” thing – we do not need advertisement’s content of the advertisement ;). The content is a view in our case. It’s needed only for reads. We do not have any business rules that require checking the content’s value. The only somehow connected rule is the one which says  that only the author can change the content, and we guard it. As I promised: we can sum this up as “CQRS in action”.

The one for the road

Can I ask you now for a bit of love <wink, wink>? Actually, a star will also work 🙂 just take a look at the project’s code and hit a star if you like it:

https://github.com/stolarczykt/DevMountJob

If there’s one thing you can do today then please do yourself a favor and visit this repo and check how things are implemented with every new post. Just dive in there, clone it, run some tests, run the app. In other words: play with it and have a great time. See you next time!

Subscribe to the newsletter, so you will be the first to get to know about new posts!

* indicates required

Design Level Event Storming with examples!

We covered a lot of ground. We started with the domain description and mistakes that I’ve made. Then we did the Big Picture Event Storming (BPES) to get the helicopter view of the whole business process and its events. Finally, we split this process into smaller units in the latest post using the Process Level Event Storming (PLES) technique. Today we are going to propose some designs for our solution. We will use Design Level Event Storming (DLES) to achieve this goal. At the same time, this is the last post about Event Stormings and next time we will be implementing our system.

First of all, I have to admit something: I love abstract thinking, and hardly ever do I have problems with it but this time was different. I have surely been an excellent example of the novice in the Dreyfus model of skill acquisition, no question. I expected some simple rules, flows, and conventions while putting the context and the goal on the back burner. I was looking for some notation at a push. All of that combined with my internal need to name things and abstractions, made me feel pretty confused. Having spent many hours in Miro, trying to create some models, looking at some other examples of DLES, and a couple of calls with Lukasz and Mariusz, eventually, something “clicked”. Well, it turned out that on the Design Level, I should… wait for it… design ;). An Event Storming is a tool used to visualize the business process, and there could be different designs supporting this process. That being said, there are different approaches to visualize those designs. We can choose/create notation or convention based on the project context, the workshop’s goal, or our preferences. The most important thing here is to visualize and propose the model that we think suits our needs best. That’s the difference between PLES and DLES. In the PLES, we were focused on processes, how they should work. Now on DLES, we should focus on models that support those processes. I just couldn’t get it.

The matter of taste

When it comes to modeling, let’s think about how we can use our sticky notes to do it. I’ve seen a couple of different approaches. I want to show you one that I’m going to use here and a few others so you can compare them.

Let’s start with a go-to book when it comes to Event Storming by Alberto Brandolini. Unfortunately, the chapter about DLES has just 10% of its target capacity, so there is not too much information about this workshop phase. The main message here is that we finally need to make decisions about the design. Even if we find something new, we need to develop an idea of how we want to solve that. It could be a bit hard, as in this part of the workshop attendees are often technical people, and we all know that we can talk about different approaches for hours. Nevertheless, we need to focus on proposing some solutions that support our business process.

In DLES, we introduce a yellow sticky note which indicates an aggregate. Brandolini suggests postponing naming it, and I tilt to this suggestion. Sometimes the first name “hides” the actual responsibility of the aggregate. Regarding the general approach in this part of the workshop, Brandolini presents a slightly modified version of the image we’ve already seen in PLES:

Source: Introducing EventStorming – Alberto Brandolini

I think it’s a good moment to share some of my thoughts now. There is not too much said/written about DLES in the book itself, and mainly this picture drew my attention. Unfortunately, I fell into the trap of using this picture as a schema to modify my artifacts from PLES. In other words: I wasn’t thinking about creating design but about putting a yellow sticky note into processes. I think I’m not the only one who was trying to do it this way. When I was looking for examples of DLES, I often met PLES artifacts/thinking with one extra sticky note. That’s why it’s a good idea to participate in the workshop with an experienced facilitator who can explain the difference between DLES and PLES.

Finding aggregate boundaries or proposing other designs/models in places where there are no aggregates will be my goal in DLES. That’s why I will use the notation I used with Lukasz in one of the previous projects but bear in mind that you can modify it to your needs. You need to visualize your models in a way that is easy to understand, even if you get back to the board after some time. It’s you who are going to implement those models, so everything has to be straightforward for you and your team.

Let’s get back to the approach I will use here. In general, I want to gather business rules, commands, and events that “occur together”, no matter where they are in the process (in PLES). I also intuitively know that I usually have some model that aggregates those elements together. That’s why most cases will look as follows:

From the left: we see commands, then business rules, then an aggregate, and finally events that are thrown by the aggregate.

Another approach I’ve seen lately is the one where you have events below the commands. Thanks to that, we can have commands ordered by the time when they occur. So, for example, in this approach the previous case with the advertisement, we could model as follows:

You can see the rectangle that more or less shows the aggregate boundaries, so we know which commands and events are connected with it. 

The different approach I would like to show you is the one from the ddd-by-examples/library repository. Jakub Pilimon and Bartlomiej Slota use the Example Mapping in their DLES:

And the final result looks like that:

The last technique I would like to mention is the one I’ve spotted on  Mariusz Gil’s Miro boards. Mariusz takes modeling a step further, and instead of presenting boundaries, he literally writes code entwined with sticky notes of commands and events. It causes a wow effect, as you can see the code even before sitting to your IDE. I encourage you to take part in Mariusz’s workshops so that you can see this approach in action. 

Let’s get this party started

Now, after discussing all those approaches, we are going to do some work. Finally, we want to propose models that, based on our current knowledge, support processes we defined in PLES.

Advertisement’s preparation

Let me remind you how this process looks after PLES:

I can tell you that you will witness quite a revolution here. Initially, I was thinking about some model of a draft. I wanted to support situations when the user starts filling up the form, leaves it for some time, and gets back to it after a few hours/days. Keeping a draft would prevent starting from scratch. I assumed that I needed some object here that would react to content changes, and if there are none, it will expire after some time. I also had another requirement saying that a draft should work for both: logged-in and logged-out users. It means we would identify drafts based on the id kept in cookies. I did not require support for multiple browsers/computers. Given all that, when I started to design the model I realized, that:

  1. we need to create a read model of the draft on the backend
  2. we need to refresh this model each time a user changes its content on the UI
  3. we need to remember to remove it from DB when there are no changes from UI
  4. we retrieve the draft for the user based on the value kept in the UI/browser

Can you spot what’s wrong here? Firstly, if the UI/browser controls the whole lifecycle of the draft, why do we even need it on the backend? Secondly, all that functionality could be done by a few lines of code on UI with local storage support. What’s interesting here is that we can support the process without the explicit model of the draft. We can just use what the browser gives us out of the box.

When we don’t care about the draft, we can skip everything that is before the payment. When it comes to the payment itself, I have just two requirements:

  1. no matter if the payment succeeded or failed, the advertisement has to be published. It means that we can publish the advertisement when we start the payment process. This decision simplifies the whole process.
  2. if the payment fails we need to send the information about the failure (some details) to the Customer Service

When I wanted to follow the notation I described in the first chapter, I got something like this:

Some of the commands that change the payment’s state are triggered after responses from the payment system. Moreover, we request the payment in the external system after we have created the object of payment. Unfortunately, the readability is not so good here, so in DLES, I decided to not stick to my general notation but to show the chronology:

The difference here is that the sticky note with payment’s aggregate occurs twice. So now it’s easier to see when we change its state. I write about “aggregate” here, but conceptually, it would be the process manager if I were to publish the advertisement only after successful payment. Nevertheless, in both cases, I would probably call it the “Payment”.

Publication

During the PLES, we came up with such a publication process:

We call it “publication,” even so it’s about hiding/putting on hold and resuming the advertisement. Maybe you somewhere have seen a similar process under the “availability” name. It’s pretty generic and occurs in this or slightly different form in most of the domains. I’m still not convinced of the “publication” as a name of the Bounded Context, so it can change when I start the implementation. 

When it comes to the model of this part, I would see it like this:

Registration

In the previous image (the one with the advertisement’s model), you could notice such a piece:

On the other hand, after the PLES, we had something like this:

You might have noticed that there is usually something more than just the “registration”. This part of the system usually is responsible for user management and grows to a whole module or even a separate service. Initially, I was also thinking about it this way. For example, I was thinking about the case where the Bounded Context responsible for banning recruiters would also communicate with this module.

Those “accounts” modules are often developed from scratch, have additional integrations with external services, and keep users’ data. At the same time, such a module exposes API, used by most services in the company. Even though sometimes the user management itself is delegated to some external service. We could model this case like that:

Nevertheless, this approach has some downsides. First of all, it’s an additional module. Secondly, API has to be implemented in such a way that all other services can use it. That’s why I decided to use another approach in this project. I will probably use something like Keycloak, letting other modules integrate “directly” with it. But, of course, as we don’t have control over Keycloak’s API, each of the Bounded Contexts will have a tiny Anti-corruption Layer (ACL). The advantage of such an adapter is that it’s tailored to BC’s needs. Thanks to that, “Publication” will use the registration functionality, and “Banning” will use only block/unblock functionality.  

Search

For now, we will have here just an ACL that will communicate with the external service. Communication will be synchronous here.

Offering

When recruiters find an exciting advertisement, they can make an offer to the developer. The most important rule here, the clue of my whole idea, is two things: 

  1. a recruiter has to accept all developer’s requirements,
  2. a recruiter can make only one offer per advertisement.

Other rules specify primarily who and what can do with the offer. For example, it means only the recipient can reject the offer or add it to favorites. So our offer model could look like this:

“Self verification”

In the first post about Event Storming, you may recall that I totally messed up the idea of this part. I was using events that I had no access to. I mean, those were, in fact, external system events that occurred outside of my domain. Fortunately, it came up quite early. During the PLES, I knew that I just needed to query the external systems about whether the developer solved any tests.

When it comes to the design, it looks like we need two things here. First is a simple CRUD for the links (URLs) to tests in the external system. The second is an integration with the external system that can query it on-demand and give us the list of tests solved by a specific developer. Having those two parts will allow us to manage views with the developer’s badges.

Banning recruiters

Banning is for sure a process that I would skip in the MVP phase. But, nevertheless, we’ve had a lot of discussions about this part, and what’s more, it has one interesting quality: in general, it’s actually a consumer complaint. So that’s why you may already see this process in other domains. Take a look at how I designed it during the DLES:

We can spot two parts here:

  1. On the left – probably some kind of process manager, as it’s quite an asynchronous process. It can be initiated by a developer, who felt scammed in the recruitment process, or contrary – wants to commend the recruiter. The developer fills up the “review form”, where they rate a person who made an offer. We send this review to an external rating system, which recalculates the overall recruiter’s rank. When we get the response and see that the rank drops below some threshold, we create a complaint.
  2. On the right, we have the “complaint” itself and its state management.

Even though we call this part “banning”, you can see that we have two concepts here: ranks and bans. We could split them into two separate contexts, but for now, we are interested only in negative ranks, so we will stay with a single context.

Food for thoughts

As developers, we design every day. We may not be aware of it, but each method, each class, has some design. Unfortunately, we rarely visualize it in any form. Instead, we keep it in our heads, where we build huge models and connections between them. It seems that we understand all that entanglement pretty well, as we notice many implementation details and business rules. But, in fact, all that is usually a misleading impression. We realize that when we start to “dump” all those models from our heads to code. There is always something that doesn’t fit.

Design Level Event Storming gives us the advantage of experimenting with each idea that comes to our mind. There is no doubt that we have forgotten something on the way back from lunch ;). Our models are still on the board in the form we left them there. We can move sticky notes around, check different approaches. Thanks to that, the final models that we’ve decided to move to the code are more thoughtful. Of course, our models can still change during the implementation, and we even have a name for that: modeling whirlpool. But those changes will usually result from a better understanding of the domain than the fragility of our memory. In such a case, we can get back to the original board and evaluate a new idea by moving some sticky notes again. Artifacts of DLES are ideas for models. They are not etched in stone. Sometimes, it’s also good to implement some prototypes during the workshop to have a closed feedback loop.

That’s all that I have for you about Event Stormings. We went through all its stages, and now we are going to move models to the code. In the next post, we are going to start doing it on our fancy tech stack. I may surprise you with some of my choices, especially that it will be an “all-in” approach: DDD, event sourcing, and CQRS!!! Stay tuned! Don’t miss it and subscribe to the newsletter!

* indicates required

Process Level Event Storming: let’s get this process started!

Last time we were talking about Big Picture Event Storming (BPES) and now we have a helicopter view of our business process. Next, we need to climb down and model sub-processes that will address our business requirements and issues. We are going to use Process Level Event Storming (PLES) for that.

Back in the BPES workshop, we could notice some smaller, autonomic sub-processes within the whole process. Now we can focus on each of them and take care of the details. PLES gives us a few additional elements that will help us in this job. 

Together with my guests, we created a part responsible for banning recruiters. Then I decided to do the rest independently as I didn’t want to take more of their time. It turned out that the image from Brandolini’s book was great support for me:

Brandolini calls this image: “the picture that explains everything”, and I can’t agree more.

On the BPES level, we were using only events. Now, we will use a few new elements, which you can notice in the above picture. So let’s take a moment to take a closer look at them and the whole cycle:

  • The user, based on some impulse from the real world, decides to take an action. Usually, their decision is driven by information from the system (read model – the green sticky note).
  • The user is taking action (command – the blue sticky note), which will be handled by the system (the pink note). As a result, the event will be produced (the orange note) – information about the change in the system.
  • When the event occurs, two things can happen:
    • policy/reaction (the purple note) to this type of event, which will result in a new command
    • changes in read models, which in turn can give the user new information to make other decisions.

Ok, let’s use this knowledge in practice now! First, we will take a closer look at the advertisement’s draft process.

Draft

After the BPES workshop, this part looks as follows:

Now let’s think how this process should look like. Who should make the decision? What information should this decision be based on? Do events require any reactions? What has to happen to trigger a specific event? To sum it up: we need to use the knowledge we gained from “the picture that explains everything”. Thanks to it, we can design the process here as follows:

Before we go any further, please notice the purple/lilac notes. Those are policies – do not confuse them with a strategy/policy design pattern. Those here are the reactions to some events that should be a trigger for a command. Brandolini, in his book, describes them as follows:

“Of course there has to be some reaction to the event. We can capture reactive logic with (lilac) Policies, like “whenever we receive an order, we add the corresponding pizzas to the backlog.””

Ok, let’s get back to the modeling of the advertisement’s draft. Everything starts when a programmer (candidate) creates a draft by filling up the advertisement’s form (1). That’s the information for the system that it should store this data, in case the user wants to finish the whole process later on. In other words: we create a read model of the form (2). When the user makes any change to the form, the content of the draft will be changed (3). From the domain level, we are not interested in what exactly has been changed. We only care about the fact that it was changed so that we can update the read model. Then, when the user is done with the form and has chosen the payment option, he/she pays for the advertisement (4). Now the external payment system kicks in and eventually gives us the info whether the payment was successful or not (5). Finally, you can notice an interesting business decision: no matter what happens, we publish the advertisement (6). I decided to make this part of the process less complex. I assumed that the failed payment in an external system would be a pretty rare case. It makes no sense to focus on all cases that can go wrong and postpone the publication, at least at the start of the business. For now, let’s publish anyway, and when we eventually get the info that payment failed, we can send this information to the Customer Support. They can decide what to do with the advertisement. Maybe they will retrigger the payment, remove/block the ad or contact the developer to clarify the situation. If we get the information that there are too many such cases, we should solve the problem then. For now, let’s do it the agile way 🙂

It’s worth noticing that when we create a draft, another parallel process kicks in (7). Its responsibility is to remove all overdue drafts, not to store them too long in our system. So, first, we give them some time to live (TTL). Then, each time the draft is updated, we renew that time (8). Finally, we remove the draft when TTL is exceeded and the developer did not publish the advertisement (9).

Registration

Every single app has a registration, right? It’s quite a generic process, so that’s why we didn’t care about it during the BPES. It means that I can’t show you the “before” state. Even after PLES, there is not too much to consider:

Usually, there is a belief that “our project is specific”. That’s why so many applications implement a separate service/app called something like “Accounts”. Nevertheless, in practice, it turns out that a service like Keycloak could often replace it. That being said, we are not going to overcomplicate this part. Even if we don’t use any off-the-shelf solution, we will simplify this process to a single step.

Publication process

After the payment, the advertisement can be viewed and searched in the system. Nevertheless, its life cycle ends when the paid time expires. During that period, the advertisement can be unpublished and published again due to different reasons. That’s why we decided to call this whole process: the publication process. Quite often, when we notice “published” and “unpublished” states, we can try to model the separate availability subdomain. However, we will not do it now, and we will stay with a bigger publication subdomain.

Let’s describe this process step by step. After the payment, we get the request to publish the advertisement (1). We react to this by making it available for searching and viewing (2). When we publish the advertisement, we also start the period when it is available. When it ends, we remove the advertisement from the service (3). To keep this process simple, I again decided to skip the cases where the user could extend this period or create a new advertisement based on the old one.

When the advertisement is published, two things can happen:

  1. A developer can decide about unpublishing it (4). This can happen when there is still some paid time available for the advertisement (it doesn’t make sense to unpublish it when it is already “outdated”). Then we put the ad on hold (5). It means that the user’s list of inactive advertisements is updated. Based on that list, a developer can decide to publish it again (6).
  2. Another case when the ad can be temporarily put on hold is when Customer Support does it. The whole process looks similar to the previous one, but different people make decisions. Moreover, they do it based on other premises or data. Customer Service can put the ad on hold when they get some complaint about it (7). 

It’s noteworthy that we will not decrease the paid time when the advertisement is on hold in both cases. Instead, we will start doing it again when the advertisement resumes.

Just to remind you how this part looked like after the BPES:

Search

It can happen that something that was an event during the BPES isn’t actually an event. An excellent example of it would be the “Advertisements found” event.

Search has nothing in common with the change/modification in our system. That’s just a query. Then, based on the available filters, the recruiter asks the system about the advertisements that meet the selected criteria. That’s why on PLES, we decided to represent this part as follows:

Offering

Let’s focus now on the key process – offering. That’s the part that differentiates our solution from the typical approach. First, a recruiter will have to accept all developers’ requirements if they want to offer any. Then, the offer can be read, rejected, or added to favorites.

After a PLES session, this process looks as follows:

Everything starts on the advertisement page. Recruiter, seeing it, can make an offer (1). They need to accept all requirements and give some contact details. Then the offer details are available to the developer on the offers page (2). This page can be a “starting point” for a few different actions. The most interesting one from a modeling perspective is “Mark as opened” (3). Notice that the UI triggers this one. You may ask why.  So UI will present the new offers as… new ones – not “read”. Then, the developer can click any of them to see the details. This action itself doesn’t change the system’s state, as in this case, we just query the system for more information. Even more, we could already have those details fetched but “hidden” by UI. That’s why after displaying (sometimes after 1-2 seconds) those details, UI sends the command to change the offer state to “opened”/”read”. You could see similar functionality in email clients.

A developer triggers the rest of the commands on this page. Those can be: rejecting the offer (4) or adding to favorites (5).

“Self verification”

This process was quite interesting already in the Big Picture Event Storming. That’s because we cleaned up many events specific to the external system and not ours. To remind you how it looks like after the BPES:

The event informs us about the start of the verification process. What we want to achieve is to give the whole process on our side some TTL (time to live). All that sounds a bit… complex. There is also still a significant coupling between us and an external system. It became even more complex during the PLES part. That’s why I ended up with a much simpler solution:

In this approach, we do not rely so much on the external system as in the previous one. Foremost, we don’t assume this system will expose any information about verification progress on their side. The only requirement for integrating with the external system is that it exposes the API with available tests and tests solved by the specific developer. But let’s follow this process from the beginning. First, the developers go to the badges’ page and see badges they can achieve (1). Each of these badges is linked to the appropriate test in the external system. It means the developer gets redirected to the external system and can start solving the test there. The whole process happens there, and we should not even care about what’s going on or how long it takes. When the developer is done with the test, they can get back to our system and initiate the synchronization (2). It means that our system will query the external one to get the tests’ results for a specific user. Based on the response, we can check whether the developer has achieved any new badge (3). If yes, we update two views: developer’s badges and badges to earn (4).

Banning recruiters

Last but not least, we have a process that we found the most interesting. Actually, we were modeling it in the first instance.

This part after BPES:

On PLES, it ended up as follows:

After the recruitment process, we want to give a developer the chance to evaluate the recruiter who has put in an offer. We assume that the trigger for that action could be something outside of our system (1). It could be, for example, a total mismatch between what the recruiter accepted in DevMountJob and the real offer. The developer, based on the experiences during the recruitment process, evaluates the recruiter. Then evaluation gets to some ranking system (2). This system recalculates the overall rating for the recruiter. Suppose the value falls below some predefined threshold. In that case, we should send a complaint to the Customer Support (3). Now process forks into two paths:

  1. The note has some time when it has to be processed (4). If Customer Support doesn’t handle it within this timeframe, we will need to handle it. It will get to the list with overdue notes. Thanks to that, we can monitor how many requests are not handled in a given time. We can introduce some notifications when there are too many of them or whenever the new one appears on that list.
  2. The note is processed in a given timeframe. Customer Support, based on the history of the recruiter and all other evaluations given by the developer that did the last evaluation, can make one of the two decisions:
    1. ban the recruiter (5) – the recruiter will get information (probably via an email) with the contact details to clarify the situation. Then, Customer Support can unlock the account (6)
    2. reject the note (7) – Customer Support can do it after analysis of additional data. For example, some evil developer could intentionally decrease the recruiter scoring by creating a lot of negative notes 😉

This model we got for the banning process is actually quite generic. I mean that there are many processes in other domains that could be designed the same way. For example, the refund process, to name one of them.

Congrats!

We’ve just done a hell of work! We designed step by step one process after another. We introduced new elements so that we could focus on details. Thanks to that, we could also validate previous assumptions.

Good job!

I bet many projects would benefit from designing their processes on Events Storming workshops, so please help me share knowledge about this technique by sharing this post!

Next time we are going to focus on Design Level Event Storming! Don’t miss it! Subscribe to the newsletter, so you will be the first to get to know about it.

* indicates required

This could be the biggest post about Big Picture Event Storming ever! And with examples!

It’s time to dirty our hands. In the last two posts, I presented the failure of the project. I described the idea of the business model and the requirements I had at that time. Now I would like to add a plot twist. Let’s use this business knowledge and practice some modeling techniques and the DDD approach. In this and the next two posts, we will be working on Event Storming. We will take the described domain/idea, and we will use it as a base for the workshop.

Event Storming – why should I even care?

If you saw a picture of a bunch of people standing in a room and putting their colorful sticky notes on the wall, it could be an Event Storming session. It may sound like another soft skill workshop, but we couldn’t have been more wrong in this case. Event Storming is a powerful tool for business process modeling. Not only “business” as there are even stories about people using it for planning their wedding 😉

But why the sticky notes on the wall? Because it is the easiest thing we can do. At the beginning, your only job is to put the past sentence (business event) on the note and stick it on the wall. This together with the subsequent process stages make a quick workshop session that can expose the lack of knowledge about our business process. Moreover, it’s a great way to share domain knowledge between participants. That’s why it is essential to invite domain experts, a dev team, business people, and anyone else who may have an interesting perspective on the topic.

Having a facilitator who has experience in running such workshops can also be a game-changer. They usually have a toolbox full of heuristics (today, you will also learn a few of them), which can help participants think out of the box. Moreover, facilitators usually have a tech background in creating software systems, so they can notice patterns and solve complex problems quickly.

Event Storming session for Poznan’s community, hosts: Mariusz Gil and Grzegorz Nowicki

The Event Storming technique was introduced in 2013 by Alberto Brandolini, who still actively promotes it worldwide. His book is available on Leanpub. Even though it’s 70% finished, it is the best source of knowledge about the technique.

In my local community, we have Mariusz Gil, who promotes Event Storming a lot in the international arena. He will also play a vital role in this post, but we will get back to it later.

Event Storming at the PHPers Summit 2019 conference, host: Mariusz Gil

Event Storming levels off participants’ knowledge, and helps build a common understanding and finding issues in the whole business process. It also enables us to tackle the complexity of the problem and decision making. It is essential because the business process is often split between different components or even departments. Teams working on their “part” focus and make decisions only there – “locally.” Sometimes teams are not on the same page and, as a result, they can harm the whole process. Event Storming is a tool used to avoid such a situation and to give us a big picture. Thanks to that, we can find optimal and global solutions to our problems.

There is another huge advantage of Event Storming – it helps find patterns on a problem level, which is usually harder but brings simpler solutions. As programmers, we have many libs, services, good practices, or patterns that we use daily. In other words, we focus on a solution instead of a problem. That’s why we should know the domain we are working on, and that’s flat. We knew about it already in the 70s while researching cohesion, and nothing has changed since then. The better code you want to write, the better understanding of the process and business model you need to have. There is no one better to ask than business or domain experts. On the other hand, as tech experts, we can help them with improving and automation of some problematic processes.

Event Storming is actually a set of workshops, starting with a Big Picture Event Storming (BPES). On BPES, we want to see, well… a big picture of the business process – we want to catch sight of participants’ perception. BPES is split into a few phases, and the original pizza recipe (Brandolini uses this metaphor) has around 6 of them:

  • Kick-off – describes the workshop’s goals, a quick introduction of participants, and an explanation of how the workshop is going to look. It’s also a time for an optional warm-up. 
  • Chaotic exploration – sticking orange sticky notes to the wall. On each note, there is a past sentence that describes a business event. 
  • Enforcing the timeline – in the previous phase, each of the participants was sticking notes in different places. In this phase, it is important to order them on a timeline so that we see when they really occur in the whole process. Thanks to this, we can spot some duplicates or different namings for the same event.
  • People and systems – events are usually triggered by particular persons, departments, internal or external systems. Now it’s time to show them in our BP.
  • Problems and opportunities – till this phase, as you’ve probably noticed during the workshop, some places/events were problematic or generated disputes between participants. A hotspot is a name for such a piece of the business process. We are looking at each of them and trying to decide whether it’s a problem we should address or the opposite – an opportunity.
  • Pick your problem – as we probably can’t solve all issues straight away, we should vote to prioritize them.

We could split all of those phases into smaller pieces with specific techniques, but we are not going to do it in this post, as even Alberto encourages us to experiment and work with approaches that work for us. He also points out that those phases can be different when we model an existing process, new functionality, or a startup. If you want to know more about the whole technique and go more in-depth, I recommend reading Brandolini’s book.

We will have our pizza recipe there, let’s see how it will turn out. Hopefully, there won’t be any pineapple on top 😉

The next stage after Big Picture Event Storming is Process Level Event Storming (PLES). In PLES, we focus on the solution, and we model things like we would like them to be. Moreover, a few new concepts will pop-up, like commands, read models, and business rules.

The last part of the workshop is the Design Level Event Storming, where we create a final model that can be moved 1:1 to the code. It’s quite a technical part, so usually only developers participate in it. We introduce an aggregate term here. We can also notice one more advantage of the Event Storming – it’s easier to change names or models on sticky notes than in the code. If something just doesn’t sound right to you, you throw it away or replace it with a different sticky note.

Find problems when modeling, not while implementing. Host: Mariusz Gil

Regarding the workshop itself, it’s essential to keep in mind the purpose of doing it in our project. In other words, we should define the goal before running the workshop. Sticky notes on the wall are the visualization of the process and not the goal itself. The goal is what we want to achieve by having such visualization. It may speed up the process, its automatization, or the list of issues that occur in the process.

A titbit about my failed project

I didn’t mention that before, but it relates to this post very well, so I will do it now. From the very beginning, Andrzej Krzywda was supporting me in my idea. He ideally joins the roles of the programmer, entrepreneur, and marketer. I will tell you a bit more about Andrzej later on, but now I just want to say that he showed me how important it is to think about marketing and selling the product and talking with business people. 

One day, we met with Andrzej in his house next to the forest, and he suggested that we could do a quick Event Storming of my idea. We didn’t have any agenda, phases, nor conventions regarding the colors of sticky notes. We just grabbed them, and we started to produce some events. After a few minutes, we had the following result:

You don’t have a wall? Use the door!

I focused only on the system’s events, like “advertisement was created” or “offer was made.” On the other hand, Andrzej was thinking about events that occurred in the developer’s life and pushed him/her to look for a new job. As a result, events like “Toxic PM detected,” “Ruby/Elixir experiment started,” or “Interested in remote” popped up.

Some other ideas appeared as well. For example, we could analyze some events, like the one about adding a new advertisement and notice different types of people on the HR side. Let’s assume that a CTO is looking for some tech leads. He is notified each time an advertisement is added when the developer has finished some architectural courses/certificates. Another idea was to have a widget that could be included in external blogs and display advertisements of developers who have skills matching some post keywords. The reason for that was that people looking for developers to their teams also read blog posts :). Both of those functionalities could be paid.

This session was the biggest Aha moment for me. As a tech person, I was focused only on the system events, which is fine when it comes to Event Storming as we want to model the system, but Andrzej showed me a totally different perspective. He was thinking of how I can sell my product and get some traffic.

I decided to mention this whole story here, as I think it’s a great introduction to the next chapter. Long story short – you don’t have to stick to any strict form of the workshop. It’s also a visualization tool that you can use for different purposes.

Our form of the workshop

Alberto Brandolini says that an Event Storming workshop should not have a fixed form, and he encourages people to experiment with it. Giving it a structure and following some defined steps is a common mistake. For example, if you have something in your domain that is important and deserves its own notation – go for it! There are some default meanings of colors, like orange for events, or blue for commands, but if for some reason, they don’t work for you, don’t let them stop you, and use different colors. To give you an example, Mariusz once ran the workshop where they wanted to visualize the processes of DB operations in the application. To do that, they decided to assign specific statements (INSERT/UPDATE/DELETE/SELECT) to particular colors.

It is worth mentioning that experienced facilitators play an important role because they usually have a rich toolbox of techniques and heuristics. They can use a different set of those tools based on a group of attendees or the project type.

Although we don’t have to run all phases of BPES, two of them almost always occur: chaotic exploration and enforcing the timeline. We decided to go just with them to run BPES of the domain of this posts’ series. I wrote “we” because I invited three other people to support me with it. Each of them has huge experience in programming and gathering business requirements from customers. Ladies and gentlemen, may I present to you:

  • Mariusz Gil – I told you he is going to play an important role here ;). Mariusz has been in the IT industry for over 20 years. He is passionate about designing and implementing systems with complex business requirements, machine-learning, and solutions that give real business value to his customers. Mariusz takes to conferences like a duck to water and is extremely outgoing. If you meet him at a conference, don’t be surprised that you will be talking about totally different things than IT after a few mins, and you will feel like you’ve known him forever 🙂
  • Łukasz Szydło – he is one out of three mentors in a best-selling ($3M) online course for software architects in Poland. Programmer, architect, trainer, and consultant. He shares his knowledge at conferences. I’ve known Łukasz since my start in IT – he was my first mentor, and I’m delighted that I can still work with him sometimes in various projects. 
  • Andrzej Krzywda – founder of the Arkency software house, which is well-known in the Ruby community all over the world, organizer of wroclove.rb conference, speaker, writer, blogger. As ardent OOP enthusiasts, we like to whine about its current state, even though our points of view may differ a bit. Usually, we do it while hiking together. Lately, we did a live code review session of the Rails app, and you can watch it here.

As we know each other, our Event Storming sessions were quite casual. Thanks to them, we also had a chance to talk and share some thoughts and experiences, which was cool in those COVID times. Sometimes we were drifting and touching different topics, but as a result, we shared a lot of knowledge, and now you, my dear reader, will gain from it.

Before the workshop, I had some business requirements, and you probably know them if you read previous posts. Nevertheless, because I want to give you as much technical and practical knowledge as possible, I was open to different approaches/solutions. Considering our experience, I was pretty sure that the original idea could change a lot. That’s actually one of the most significant advantages of Event Storming – depending on how open we are, it can lead us to changes/improvements/optimization, even on the level of the whole organization.

Due to COVID, all workshops had to move online. Before COVID, there were discussions about whether it makes sense to run such a workshop remotely. Now it is a new norm. It turned out that thanks to great tools, it works pretty well. In our sessions, we used Miro, which became the standard tool for the purposes of Event Storming. 

If you want to listen more about the remote aspect of ES, you can listen to one of Mariusz’s podcast episode. He is talking there with Alberto Brandolini, and I encourage you to listen to it (the episode starts in Polish, but around 04:55 it switches to English).

OK, let me show you in great detail our ES sessions now.

Ready, steady, go!

Before our first session, I sent the domain description to the guys, and I uploaded some original wireframes to our Miro board. We had them for reference, so I could quickly explain the flows on the example. 

Then we started chaotic exploration. Each of us was putting events on the board. First questions, ideas, and discussions popped up. We decided to mark “nice to have ideas” as green notes. It’s a good practice as it usually unleashes creativity. After a while, our board looked as follows:

Please take a look at it and try to refer to the domain description from the first post. You can spot here that some events are organized into some kind of batches/groups. That’s the result of using a “Bulk Mode” feature in Miro. It is a useful functionality in the early phase of a workshop because it allows participants to add a few events at once. You add them as rows in a separate modal form, and after a submit, they are added as “sticky notes” to the board. 

All events were added in quite random order at this stage, so we started the next phase – enforce the timeline. My task was to narrate the whole story. At the same time, I could add some additional information about some pieces, so we had a few extra discussions. Then, Mariusz presented a heuristic that he calls “fantastic four” or “0, 50, 100, 150”. We can usually use it when we observe a  “binary” event – something either happened (100%) or something didn’t happen (0%). Sometimes we have two additional cases: something happened partially (50%) or there was too much of it (150%). In our case, such an event was “Advertisment’s requirements were accepted.” I had a yes/no mindset here: the recruiter accepts or disapproves the requirements. Mariusz suggested that we could try to use the “fantastic four” heuristic because in the real world, in such a situation, there is a place for negotiation. For example: “we don’t allow remote work, but you can bring your pets into work.” We wrote down this idea to consider it in a later part of the workshop.

Another typical example when this heuristic can be used is a payment process. Intuition tells us that we have only two options here: payment succeeded or payment failed. In fact, in the real world, we can have at least four cases:

  • 0% – payment failed
  • 50% – only a part of the payment was paid – due to some typo
  • 100% – payment succeeded as it should
  • 150% – user paid more than needed (typo?) – now we need to trigger a process of some refund

OK, let’s get back to the rest of our events now. We have them in the correct order now. We didn’t remove any duplicates nor needless events yet. Nevertheless, we marked some smaller, quite independent parts of the whole process, telling us about the possible subdomains. It will help us focus on smaller pieces in the Process Level .

At this stage, the board looked as follows:

Below you can also see our 90-minute session in a 90-second video:

In the next step, we put a line to separate from the results of our work. We started to put only domain events under this line – events that cause some real changes in our system’s state. At the same time, we removed some duplicates. We also agreed to skip events from functionalities that will not be implemented in the first iterations. As a result, we were able to visualize our first business process and business model.

To remove all “unnecessary” events, we used another helpful heuristic that concentrates on the event types. Mariusz told us that in his workshops, he distinguishes between 4 types of events:

  • environmental events – exist outside the system – in the real-world, for example, a person entering the shop. Usually, they are not crucial for the workshop, but they help to get the context.
  • UI events – we are clicking/using the UI, but we don’t “submit” our choices. We don’t change the state of the system. As an example: we can choose between pricing plans on UI as many times as we want, but until we confirm our decision, nothing actually happens in our system.
  • infrastructural events – refer to technical things and don’t impact the system at all: “Event to Kafka sent”, “Metric increased”.
  • domain events – the heart of the Event Storming. We should focus only on those in our workshops.

Using the above assumptions and heuristic, we changed the Big Picture as follows:

Let’s go now piece by piece, from left to right, and see what changed. 

At first, we removed all events connected with lead generation: 

You won’t find them in the After section because they don’t belong to our system (at least the one where we have advertisements). They happen “before”, so they can occur in the funnel. They will be essential for the marketing/sales department. We can think of another small app that measures the level of developers’ burnout. When the app recognizes that you should change your job, it can lead you to DevMountJob. 😉

Now I’d like to draw your attention to an interesting reduction of events:

Here you see in action the second heuristic I mentioned – the one about different types of events. We removed all UI events. Notice that “Added a certificate” or “Marked remote job as required” don’t change the system state at all. Those are only actions on UI. In other words, we are working on a draft of the advertisement – we are preparing it. We can describe all those events as one: “Draft was modified”. Actually, we could have no events here if we decided to have no draft concept in our system. Nevertheless, we want to have it because we want users to continue the preparation process even if they leave it for a while. Otherwise, we could remove all draft-related events, handle everything by the UI, and have only a single event when the form is submitted, and the advertisement gets to the system.

When the advertisement is ready, we get to the payment and publication process:

We have some changes here as well. When reading the domain description and my requirements, you could have the impression that I didn’t have a clear idea as for that part. That’s why we had a long discussion. Lukasz pointed out that if the advertisement is available for some period, maybe instead of going on with some pricing plans, we should sell time. If a user wants the advertisement to stay longer in the search results, then they pay a few extra dollars. We liked it, and that’s how the first business model was born. Thanks to that, we could skip all that part with pricing plans. By the way, we also put the hotspot (pink sticky note) here just to mark the place where we had some concerns.

Initially, I also had an idea that after the publication, the advertisement is read-only. I simply wanted to prevent the situation when the recruiter accepts the developer’s requirements, but then they are changed, and we have some inconsistency. In practice, the read-only mode could be really annoying. Imagine that you made a typo and want to fix it after a day or two. The first idea to solve this issue was to give a user X mins after publication to edit the advertisement. That’s why you can see a “Time for adv. edit expired” event in the first version. But then we realized that the recruiter accepts the developer’s requirements that he/she has at a given moment in time. It led us to the idea of something like “snapshot” – an offer made to the specific requirements. 

Now let’s move to the offering:

Do you remember the idea of negotiations? Well, the business didn’t get it 😉 and decided that the recruiter needs to accept all developer’s requirements. We left a note about that and we could move to the last two processes: banning recruiters and developers’ skills verification. It’s important to mention that you can see them as the last ones on the time axis if you look at the whole picture. Nevertheless, each of them can occur at any time in the entire process. Sometimes it’s good to mark such a process with an indicator, but we didn’t do it.

We are going to take a closer look at banning now:

Notice that only the wording of two events has changed:

“Ban request sent” was changed to “Ban requested”

“Unban request sent ” was changed to “Unban requested”

Word “sent” slightly suggests a form of the activity. In the first version, we meant here some kind of HTTP request sent from the HTML form. In practice, in such situations, we should be opened to other options. For example, a ban request could also be made via mail, phone call, Slack, or fax, you name it. It is a small detail but often helps to notice approaches we’ve never thought about.

Now it’s time for the last process – candidate’s verification:

I decided to include this process in the first version of the product, as it shows how to integrate with the external system. Moreover, it is a long-running process. As advertisements are anonymous, I wanted to give developers some optional possibility to show their skills. In other words, they could get badges after solving some tests in the external systems like DevSkiller. You can notice a reduction of events specifying readiness and the start of the test. That’s because those events are outside of our domain, and they belong to the external system. We would only want to start the verification on our part and get the test result when it’s solved.

That would be it when it comes to our Big Picture Event Storming. In the next post, we will do a Process Level Event Storming and model each of the processes in detail – solution space. Now a quick summary and the homework 🙂

Summary

Even though the Event Storming workshop has a simple form, it’s not easy itself. It requires business thinking, which may be totally different from what we have in code or UI. It helps us to notice real business events that occur in our domain and change the state. 

It’s a good idea to participate in the Event Storming workshops led by an experienced facilitator (like my guests). I encourage you to do so!

Now it’s your turn!

I hope we’ve piqued your interest with this technique and that you want to experiment on your own now, so it’s time for a call to action 🙂

Conduct a Big Picture Event Storming workshop. You can even do it alone, but the more people you convince, the better. For modeling, use the business process that you support daily. If you don’t want to use this one, you can use mine – there is enough information to do it in this and the previous posts. No excuses! 🙂

Enjoyed reading this? If you would like to see Domain-Driven Design and Event Stormings on a real-life example, don’t miss the next posts. Subscribe to the newsletter, so you will be the first to get to know about them.

* indicates required

The story of how to NOT start a project.

In the previous post, I described in great detail what we are going to do in the next few months. Today I would like to give you some insights into what was going on in my head when I was trying to launch that SaaS idea. This post is some kind of “behind the scenes” type of text. I’m going to show you how the idea was born, how I “validated” it, how much time and effort I invested in it, and finally how I failed. To sum it up, I will show you how to probably not start the project.

An idea was born

Like a lot of great ideas in this world, also this one had its dawn of time in… a drunk night. Well, I met my friend after a few months, and he said it would be cool to have a headhunter company. That company would verify developers and put them on their website in some kind of “auctions.” You may think of it as Amazon or eBay with developers ;). The idea popped up after I told him about the money I earned from recommending my friends for some job offers. As you can imagine, after a few shots, we were really optimistic about our great business model. We even talked about it a few times after that night. But an idea without execution is nothing, so suddenly it failed for its first time.

Later on, I was working on a project where such a reversed model of search used to work pretty well. That gave me extra energy to try one more time. I changed the idea quite drastically, as I wanted to remove everything, to skip the verification process and the need to run a headhunter company. The new idea was to just give developers the ability to add new advertisements – a simple CRUD application. On the other hand , there would be a search feature that recruiters would use. A few days of work and I could live my rich life ;).

At the same time, I read articles and listened to podcasts that were talking about problems in the hiring process. After “research,” I got to some conclusions that you can find in the previous post, so if you haven’t read it yet, catch up with it :). The idea was born at that stage, and my side project got the “DFS” codename, which stands for “Developers For Sale.” Yup, I know, sounds scary, but I can assure you – I was not objectifying developers ;). I had to think about a new name, and we will get to that soon. First, I would like to show you my business validation.

The idea’s “validation”

A real entrepreneur knows that before doing anything, the business idea should be validated. As an aspiring entrepreneur, I decided to create a pool. The problem was that I was pretty fixed on my vision of the final product, and I didn’t want to change it in any way. Instead of asking about pains in the hiring process, I asked users to anonymously leave their skills, desired location of work, and how much they would like to earn. Thanks to this, I was able to validate whether people are not afraid of sharing such a piece of information.

Ok, so at that point, I was set about what I was going to ask, but I had no idea where I could let people know about the pool. I had no audience at all. Asking people directly or on any Internet groups was a blocker for me. Luckily, a friend of mine is an IT blogger/vlogger, and he allowed me to put a short video on his vlog. That was actually an excellent idea, but at the same time, that’s the end of my entrepreneurial skills… Any business-oriented person would grab a smartphone to record a 2-min video, then would take some tool for creating surveys and create one in the next 5 mins. But no, I know better, I have to go “professional.” I need a name, a logo, designs. Moreover, a video had to be recorded by another friend of mine who does this daily. There had to be music in the background, my name popping-up, and a big logo, at the end of the video.

As a result, the survey itself ended up looking as follows:

After filling in the form and submitting it, a user was redirected to a “thank you” page. There were social media widgets to allow sharing, and a form to leave an email address. You may think now that something is not right here – I was talking about anonymity, and now I ask people to leave their email. Believe me or not, but I was really serious about the anonymity of users in that product. That said, I can assure you, that email form on the thank you page was not connected in any way to the survey. The only purpose was to build a mailing list of people interested in what was going on.

When all that was ready, my video was added to the vlog’s episode. There were no details about the product itself, just asking people to take part in the survey. I don’t recall exactly, but the numbers were around: 300 took part in the survey, and 100 subscribed to the mailing list. That was a great success for me.

First things first

Now let’s focus on “the most important” things in running your own business: the name and the logo. How are people going to fill in the form if there is no logo and name? Yup… That was me thinking about business back then ;). Nevertheless, the story is quite interesting, so I will describe it here.

Some time ago, I was working on a small side project with a guy who got his Ph.D. in History of Art. He came up with the name and designs for that project. I really liked them and the whole idea behind them. That’s why I asked him for help, as “Developers For Sale” doesn’t sound good ;). As he is also a Linux user, after a few days, he got back to me with the idea of “mounting a new job by developer” from a command line. Like you would mount a resource in Linux. It would look like this in CLI:

I was delighted. This was not only because of a reference to a command line but also as there was a “$” sign. Moreover, “mount” can bring to mind mountains (Mount Everest), which may be a reference to the career path. Mind blown, isn’t it?

Then I described the whole idea to the designer, and after a few days, I got such visualizations:

The first one got my attention, but now when I look at them, maybe I would choose the second one, as it’s a bit retro. Which one do you like better?

Both of them refer to a command line, and both have a “$” sign. Maybe its meaning is not so obvious when you take a look at it for the first time, but I was really proud of it. In case of earning millions of dollars on my new SaaS, I was prepared to explain this deep meaning in interviews ;).

Designs

I knew what kind of views/pages I wanted to have, so I prepared quite detailed mockups. I sent them and a project description to the designer, and,  after a few days, I had the first version that I accepted. You can see them below:

We are almost there…

Now the fun begins. Long story short, while working on designs, I asked the designer to also prepare designs for the survey form. Everyone knows it has to be custom, with a logo, etc. Instead of taking some out of the box survey tool, I decided to develop it from scratch and host on Elastic Beanstalk, as I really like AWS services. So I did…

A few days later, the survey was deployed and was ready for traffic. Results I showed you a few chapters earlier.

The “post-survey” era

After initial success, I was thinking about how to get my great product even more traffic. I thought that content marketing would do the job there. My thinking was as follows: 

  1. write an article (mentioned in the previous post, link here),
  2. submit it to Hacker News,
  3. watch the mailing list grow to thousands of emails.

Well, as you can expect – it didn’t happen.

Writing an article took me quite a lot of time. I had some doubts that people reading it would hear my Central European accent, so I sent it to review to a friend of mine who is a great philologist. Fortunately, it was quite good, and only a few more natural collocations were added. Moreover, I thought that instead of stock photos, I would like to have some eye-catching sketches, so I asked a drafter to do them. Then everything was ready, I could submit my article about changing the world to HN and… nothing happened. I saw in Medium’s statistics that people were reading it, but only a few subscribed to the mailing list. Then I was sending back the link to the article in reply messages to job offers that I was getting at that time. It turned out that recruiters were quite interested in the idea. I was noting their contact details to let them know when I was ready with my product as I was supposed “to start soon.” As an idea without execution is nothing, I’ve never got back to them.

We got to the stage when I got really tired of all that “entrepreneurial life.” I was thinking of delegating the implementation of the prototype to another friend of mine. He wanted to play a bit with AWS, so we cut a deal pretty quickly as it was a win-win situation. A month later, I had a clickable prototype that I didn’t show to anybody. I think that besides personal reasons for not launching it, I also lost faith in its success. Some kind of excuses appeared, that probably would be better identified here by some coaching gurus, so let’s leave it here for them 😉

What went wrong?

You probably already know what went wrong, but let me say that loudly. I focused on totally not important things, instead of focusing on my potential clients, getting to know their pains, observing them, analyzing their behavior, supporting them, and building a community. I did research supposedly, but that’s like with any other research – you can come up with such results that will support any thesis. Worst of all, I was defending the idea only because I wanted to do it.

With the benefit of hindsight and after seeing some success stories, I can admit that those totally unnecessary activities were: logo, designs, video, drawings, article, overthinking… All that consumed too much of my time, energy, and money. As a result, nothing was left for delivering real value and solving other people’s problems. That’s why I’d advise against taking such an approach in launching your side projects. Nevertheless, every cloud has a silver lining – at least we have a lot of resources and an exciting business domain to model.

If you have any questions about any of the fragments of this post, feel free to ask in the comments section, and I will reply. I also encourage you to share your experiences as I know that I’m not alone in such an approach to side projects 😉

Last but not least, there is some really great stuff coming in the next posts about Event Stormings and Domain-Driven Design, so don’t miss it and subscribe to the newsletter below.

* indicates required

See how I failed at my SaaS idea and what you will gain from it.

Today I have a story for you. A story that would never appear in any of popular business or marketing books, because it’s a story of my failure. In other words – I didn’t do it. My idea for the great SaaS product has never come through. Nevertheless it recovers in my head from time to time. Likewise this time, but now it’s different. Instead of creating the SaaS in my basement without showing it to anyone until it’s done, I will share with you the whole process with all technical and business details. But that’s not all – hold your breath – it will be an Open Source project! That’s because I want to share with you a lot of practical knowledge about software design, software architectures, Domain-Driven Design in general, and its building blocks. Moreover, I’m going to show you how to efficiently gather business knowledge and requirements by running Event Storming sessions.
As a side effect, you will also get to know about how I wanted to make my living by selling the SaaS product to you and HR departments. You will see my motivations and how I was thinking about monetization and people’s behavior.
In the end, as this is going to be an Open Source project, I will encourage you to contribute to it by fixing bugs or forking it and implementing your own approaches. What’s more, you will be able to use it for your own purposes. If you have always wanted to be a marketing expert or an entrepreneur, feel free to take, deploy and run it as your own business. I don’t mind.

You may ask now why I want to show you everything and what I will gain from it. There are a couple of reasons for that.
First of all, it will help me to close that chapter in my life, and I will do it by creating my own Open Source project! Thanks to that, I will bump up some of my stats ;). Teaching others is the best way to learn, and more exercises equal more experience. After the whole project is done, I will gain +5 to wisdom on my character’s card ;).
I really like the business domain and the whole idea behind the project itself. Moreover, I also like the technical approach that I’m going to use here.

Second of all, I want you to learn new things from the areas of software design, architecture, and DDD. Those are usually techniques that can totally change your way of thinking about software design and business modeling, and I would like to share this knowledge with everyone.

Thirdly, it may be that the business model/idea itself is attractive only in my opinion. Some product validation would be required here, and if it failed, there would be no point in implementing it. But implementation itself is the most exciting part for me, so – no validation at all ;).

Last but not least, I actually had a prototype of the product, but I’ve never launched it. At that point in time quite a big change in my priorities happened, so building the audience and marketing of the product fell all way down on my list :). Launching and maintaining such a project is a huge responsibility, and I couldn’t afford that. One can say those are excuses, but I don’t really care. I told you at the beginning of our adventure – you would not read this story in any coaching/marketing book. You are here to learn a load of new technical and modeling stuff, so let’s go.

Our schedule

I will show you now what is going to be included in the whole post series. A small disclaimer that the order of the topics can change as well as the area of expertise will probably stretch as with me thinking about it every day, new ideas pop up :). For now, it looks as follows:

  1. Domain description. This is the post you are reading right now. The idea of it is to explain to you the whole business model. Also, my motivation to create such a project back then as well as now. I highly recommend you should read this post as it is the starting point of our journey – you need to know the domain of the problem to have a good understanding of what’s going on later on.
  2. Genesis. In the second post of the series, I will be brutally honest with you. I will show you how to probably not start such a project. You will see how much energy I put in places where I could simply do nothing. To sum it up, I will show you how the logo and designs were created, how I “validated” the idea, and my ineffective attempts at content marketing.
  3. Big Picture Event Storming. I will show you there my first event storming on that project, and you will see how cool it is to do it with a person that has a lot more business experience than you.
  4. Design Level Event Storming, where we will split the business model into Bounded Contexts and Subdomains.
  5. Architecture or even architectures’ style pick 😉
  6. Documentation – general one and for architecture decisions.
  7. Project’s setup, which in this case means package structure, VCS, and CI.
  8. Aggregates, strategies, repositories, services, etc. This one will be split into a few separate posts as there is a lot to say about each of those DDD building blocks.
  9. Commands and their infrastructure.
  10. Events and their infrastructure.
  11. Deployment.

Domain description

The domain we are going to work on is an initial phase of devs’ hiring process. To be precise, turning it upside down, so developers, instead of browsing job offers would create their own, anonymous, time-limited advertisements. They would put their expectations there and wait for recruiters with offers. To sum it up: “I’m a strong and agile/waterfall (dad joke alert) developer for sale.” I know – may sound like I was objectifying and treating developers as regular resources. Well, please forgive me – I was young and needed money ;). But if you think about it, it was actually the opposite as it was going to give everyone the same start – more on that later.

The detailed genesis of that idea will be presented in the next post, but for now, I will just say that the final form of it developed based on research I did in this area. There were, and actually still are, a lot of new articles/podcasts/videos every few weeks that point out what is wrong with the hiring process. Moreover, I was directly asking devs and people from HR departments about their experiences. It turned out that recruitment-related problems are not solely bound to the interview but start ahead of it. The issues were reported on both sides — both the developers’ and the recruiters’.

Developers’ side:

  • If not looking for a job, they would not like to keep being informed about new possibilities of employment.
  • Job adverts are unstructured – form reigns supreme over content, terms, and conditions of employment are not presented clearly enough.
  • Talking about the salary is the stressful part of every job interview. Candidates fear their demands might scare a potential employer away. On the other hand, they are also concerned they may not be sufficiently appreciated and end up underpaid.
  • There are concerns that should they come across as underqualified, they may be tempted to lower their expectations with regard to the pay during the actual interview, compared to their pre-interview estimates. Let’s connect it with the imposter syndrome here ;).
  • Another concern is that by presenting your thorough profile, you may end up being discriminated against or being offered relatively poor terms of employment due to certain characteristics (such as your gender or background).
  • Lack of information about the salary bracket in numerous ads, which makes even ballpark estimates as regards a company’s financial capabilities impossible.

Recruiters’ side:

  • A lack of a selected target group — knowing who is available would make it possible for them to come up with much more personalized offers.
  • Annoyance with no response whatsoever to either social media- or e-mail- based job offers. In other words, recruiters often end up addressing people who may not be interested in changing jobs at that given moment.
  • Having a set budget for salaries at certain positions while not being allowed to openly present it in job adverts.
  • Candidates’ demands as presented during the interview, which does not tie in with what the company can offer.

Possibly some of them have already been addressed, thanks to new job boards that present job offers in a structured way and exposing, for example, salary information. I’m not up to date with it. Nevertheless, I still find the business domain/model exciting as it is still some kind of reverse flow, so it’s a perfect candidate to model and implement it together with you. The domain is complex enough not to be another example of a pet store project.

I’ve already mentioned that my idea to solve the above problems would be reverting the initial phase of the hiring process by handing it over to developers. This could improve the process in a few areas:

  • Isolating the target group, or the developers searching for work, from the total number of developers on the market. Thanks to this, recruiters will only direct their offers to those actually interested in them.
  • Developer advertisements would be standardized and stripped down to basics, such as financial expectations or the location of your employment. It is also crucial that the candidate’s technical profile be presented in a lucid way.
  • Clarity with regard to conditions from the word go – even before the interview. Thanks to the suggested approach, both sides are on the same page right from the start. This also improves communication between them further on into the process. Additionally, it eliminates cases of developers being tempted to undersell their talent following an interview that did not go according to plan.
  • Developer anonymity. Apart from the obvious reason which is wanting to keep your current employer in the dark about being on the lookout for some new prospects, there are countless others. An example of these would be equalizing the ground rules for all involved — you present your skill set and requirements, thus creating the basis for a preliminary assessment. It ensures that those offering a job are not taking into consideration things they shouldn’t be. Thanks to this, the interview is solely based on your merits.
  • No needless networking. A notice with your offer is only displayed for a set period of time. There is no room for standardized profiles or networking here. You choose to use this tool only when you really need to.
  • Speeding up the recruitment process. Through a clear set of candidate’s demands, recruiters can see right from the start whether or not these can be met. Then all they need to decide is whether to accept the conditions and make an offer or to keep on searching for the right person.

Now we come to the point when I would like to present my business requirements regarding the new flow of the hiring process: 

1. A developer creates an advertisement:

  • The advertisement is time-limited just so as not to hang there forever.
  • A few adverts can be active for the same developer at the same time. There can be a few reasons for that. For example, a developer is a Ruby developer and looks for a job in Ruby. Still, he/she would also like to check whether with that experience they can get any offer as an Elixir developer. Then a second advertisement could be created.
  • Each of the advertisements can be temporarily unpublished at any time. For example, when we get a lot of job offers and don’t want to get more of them. On the other hand, permanent removal should also be possible. That’s in case we reach the limit of free advertisements but want to create a new one. And now we move to monetization $$$.
  • The initial idea was to charge for each advertisement. The assumption is, that if you are looking for a job, you will be willing to pay $5 for that. Nevertheless, there is one problem with such an approach – you will not pay if you totally don’t know the product and its potential. That leads me to two solutions:
    • Limit for free advertisements, and each one above this limit is chargeable.
    • Tiered pricing, where the first tier is “free” with 2-3 advertisements. Higher tiers allow you to add more of them and give you extra features. Regarding those features themselves, they are not defined at all. But I’m thinking about some advertisement’s highlighting and showing statistics. You know – a developer seeing charts equals a happy developer ;).

Because I still consider the first approach some kind of edge case, I decided to go with the second option. The other advantage of it is that tiers usually should increase your revenue, and if people have 3 options, some of them will take the lowest tier (free in this case), some the middle one, and there is also a part that goes for the most expensive one.

As the tiered approach requires some features defined and I have no idea what they could be, it would be great to make this piece of process flexible enough to support both options (or more). We will start with free advertisements and the limit for the number of them, just to postpone the decision of the final approach.

2. When an advertisement is published, it’s also available in the search component. That’s the most interesting part for headhunters and HR departments. When a developer decides to unpublish or delete an advertisement, it should also disappear from search results.

3. A recruiter can make an offer to an advertisement. A few interesting requirements kick in here:

  • A concrete recruiter can make only a single offer to the specific advertisement. That’s because we want to prevent spam – sending the same offer multiple times to the same advertisement.
  • If a recruiter wants to make an offer, he/she needs to accept all of the developer’s expectations, which are marked by the developer as required ones.
  • While making an offer, it’s required to fill in contact and company data.

4. Developers can see all offers in their admin panel. Each offer can be deleted. In this case, the developer will not get the same offer again, as recruiters can make an offer only once to the specific advertisement (requirement from point 3). On the other hand, if a developer finds any offer interesting, they can use contact details and take the rest of the communication process outside of our SaaS app. That’s actually the intended operation as we want to be as anonymous as we can, so we don’t want to have any communication or storing any sensitive data (CVs) within our app.

That’s it when it comes to the domain description and requirements. You should know now what kind of problems I have spotted in the hiring process and what my brand new idea to solve them is. I described it some time ago in a different article that you can find here. Its purpose was to do some content marketing, so I submitted it to Hacker News, but as you can suspect – it didn’t go viral ;). I leave it here for you just as a fun fact, because the most important one for our upcoming adventure is still the one you are reading right now. We have here the domain description and business requirements in greater detail. That’s an excellent base for Event Stormings that I will describe in the next few posts.

I gave you my word that I was going to show you literally everything and to be brutally honest. Speaking of which, in the next still non-technical post, I will describe to you the whole genesis of the idea. I will show you how the logo and designs were created, how I was gathering information from developers and recruiters, and finally, how I failed. To sum it up: I will show you how NOT to start such a project. All this can give you an even better context of the situation and can help you in your side projects. That will be the last non-technical post, and later on, we’ll jump directly into domain modeling.

I would really like to spread the word with the idea of such an Open Source project, so if you, too, find this idea interesting, please help me and share it with your friends.

If you would like to see Domain-Driven Design and Event Stormings on a real-life example, don’t miss the next posts. Subscribe to the newsletter, so you will be the first to get to know about them.

* indicates required

Stretch out for your cohesion

Today I have something of a warm-up. A few simple exercises that can help you define the type of cohesion that a class has. In Structured Design (for more details about the article itself go to the A brief history of coupling and cohesion post) the authors proposed a simple technique, which was to describe a module’s (class in our case) function in a single sentence. 

So now let’s look into their guidelines:

1. “If the module is functional in nature, it should be possible to describe its operation fully in an imperative sentence of simple structure, usually with a single transitive verb and a specific non-plural object.”

For example:

  • Calculate VAT
  • Calculate commission
  • Read the temperature
  • Retrieve the order info

2. “If the only reasonable way of describing the module’s operation is a compound sentence, or a sentence containing a comma, or a sentence containing more than one verb, then the module is probably less than functional. It may be sequential, communicational, or logical in terms of cohesion.” 

For example:

  • Update time spent on the task, employee’s work time, and invoice based on time card. – probably communicational cohesion, as all elements are communicationally connected by a time card. 
  • Update the order and save it. – sequential cohesion.

3. “If the descriptive sentence contains such time-oriented words as “first,” “next,” “after,” “then,” “start,” “step,” “when,” “until,” or “for all,” then the module probably has temporal or procedural cohesion; sometimes but less often, such words are indicative of sequential cohesion”

For example:

  • Before sorting, save data, remove duplicates, and check checksums – temporal cohesion as we assume that order is not essential here.
  • Download exchange rates, next send sales results to the sales department and new orders to the orders department. – probably procedural cohesion as we model the following steps where order is essential but output data of elements is not input data of those following them, so it’s not sequential.

4. “If the predicate of the descriptive sentence does not contain a single specific objective following the verb, the module is probably logically cohesive. Thus, a functional module might be described by “Process a GLOP.” A logically bound module might be described by “Process all GLOPS,” or “Do things with GLOPS.””

5. “Words such as “initialize,” “clean-up,” and “housekeeping” in the descriptive sentence imply temporal cohesion.”

Those guidelines might be considered too general, but in my opinion it is an excellent exercise sometimes to ask ourselves “what does that class actually do?”. 

What about a situation when we are considering adding a new element into an existing class? In Structured Design (the book), the authors give us a similar technique based on the principle of association. This time we should answer the question “why does the element fit there?”. As a result, we could end up with the following statements:

1. “Z is associated with this module containing X and Y, because X, Y and Z are all related by virtue of having the ‘glop’ property”

2. “It’s OK to put Z into the same module as X and Y, cause they’ are all related in such-and-such a manner”

3. “It’s OK to put Z into the same module as X and Y, cause they’re all members of the glop set.”

Last but not least, a hint from me: model problems/features in a group. Why? Since the bigger our understanding of the problem, the higher the cohesion is. However, there is a tiny catch here. Each of us analyzing a problem has their own version of it in mind and the same goes for solutions to it. That’s why it is so important to share this process with others. Through sharing, we build a common understanding of the problem within the team.

P.S. If you have enjoyed this article and would like to always have it with you offline or you would like to have some super sketch notes with types of coupling and cohesion to print out and stick them above your bed, don’t hesitate and download an ebook, that I have for you. You will find there a few great posts about rotting design, coupling and cohesion put together into PDF, EPUB and MOBI formats.

* indicates required
Photo by kike vega on Unsplash

The forgotten realm of Cohesion

For the history of cohesion go to A brief history of coupling and cohesion. Below, we will focus on the concept itself and its types.

Now it’s time to focus on the classes themselves and the elements they consist of. When do we know that a specific element fits into a certain class? Sometimes what we assume does, actually doesn’t necessarily do it to for our colleague. In such a case, cohesion comes to the rescue. In general, it’s a measure of how elements go together.

Regarding cohesion, there was a problem with what to call it to start with. At the beginning it was named “intramodular functional relatedness”, but this term was found to be a bit clumsy. Cohesion was also called “binding” or “functionality”. In his book Myers calls it “module strength”, which is also entirely accurate as it can tell us how tightly elements of a class are bonded in the context of the functionality they perform – to what extent they are related. Finally, the term “cohesion” was chosen by analogy to the cohesion of groups – a similar concept in sociology. 

Glenn Vanderburg wrote a post where he explained why in his opinion programmers have a problem with understanding the whole concept of cohesion. He argues that the name is not as  self-explanatory as is the case of coupling and it is not used so often in everyday life, either. Because of that, we can’t refer it to our daily situations. To better visualize the term, he proposes comparing it to other words with the same root. For example, “adhesion”, which describes stickiness. If something sticks to something else, it is a one-sided process, often aided by a third-party – glue in most cases. He also used an example of duct tape that doesn’t have to fit into anything as due to its stickiness it sticks to almost anything. On the other hand, cohesive elements match each other perfectly, and there is no need to use any glue between them.

The better our understanding of a designed problem is, the higher cohesion we can end up with. This is why it is absolutely vital to spend time on getting familiar with the problem itself and the business domain. Once we know it quite well, we should focus on grouping elements in a way that reflects the problem. Thanks to that, we maximize relationships between them.

Now let’s think what drives the grouping of elements into a specific class. To answer that, we should take a closer look at types of cohesion, starting with one with the lowest and ending up with the highest degree of it.  

Types of Cohesion

Coincidental Cohesion

The first criterion for assigning elements to a specific class could be an actual lack of any criteria. It means that elements are grouped totally randomly – a real free-for-all. Such classes are created coincidentally and not in the process of design.

If we were to give this type any number on the scale of cohesion, we would probably give it a zero as there is no cohesion here at all.

Classes with this type of cohesion are quite rare but if we find any, they are probably artifacts of bad modularization. Usually, they are created because we find some sequence of methods’ calls in a few different places in our application, and we decide to extract them to a single class. Extracting, as we know, is not a bad thing, but if single methods do totally different things and, worse still, if we do it without thinking about what those sequences do in each of the contexts, we probably should leave them as they are. Otherwise, we can end up with a class that we will have to modify when something changes in one of those contexts but not in others.

Coincidental cohesion – total randomness in grouping elements into a class.

Logical Cohesion

In logical cohesion, elements of a class are grouped because they solve problems from the same category. Logically there is something that they have in common even though they have different functionalities.

A good example here could be mathematical operations, as each of them can do various things, but often they are grouped in some Math or Utils class with… mathematical operations.

The above-mentioned Utils classes are usually good examples of this kind of cohesion. One to rule them all!

Logical cohesion – class’ elements solve the same type of problems. For example,  a class that aggregates all mathematical operations.

Temporal Cohesion

If logical cohesion is combined with another grouping criterion that is the moment of execution, we will probably get temporal cohesion.

The best examples of this type of cohesion would be classes/modules that initialize, stop, or clean something. Initialization methods are logically connected as they “initialize something”. What is more, those methods usually have to be executed at some specific point in time or period.

Quite often, such classes/modules need to have this type of cohesion, and any attempts to change it to more cohesive types are highly ineffective.

Temporal cohesion – elements of the class have to be executed within the same period.

Procedural Cohesion

The next type is procedural cohesion, and it usually occurs when instead of modelling a domain we focus on an algorithm of execution – the order of steps that have to be executed to get from state “A” to state “B”. It may lead us to the approach where we end up modelling the algorithm itself and not the problem that we were supposed to solve.

Cohesion in this type is higher as elements are not only grouped because of their moment of execution like in temporal cohesion, but they also have to be executed in a specific order.

Loops, multiple conditions, and steps in the code can show evidence of procedural cohesion. Those are usually classes that, while being analysed, make us want to take a piece of paper, a pencil and start to draw a block diagram. 

Procedural cohesion – usually when we model algorithm itself and not the problem that we were supposed to solve

Communicational Cohesion

The first type of cohesion that focuses on the modelled problem is communicational cohesion. The criterion for grouping elements into a class here is that they are communicationally connected, which means that they work on the same input or return the same type of output data. Thanks to that, it can be easier to reuse the whole class in different contexts.

Classes like this have origins in thinking about operations that can be done on some particular set of data, or on the other hand – operations that have to be executed to get that set of data.

Communicational cohesion – elements in the class are grouped because they are communicationally connected, so they use the same input data or return the same output data.

Sequential Cohesion

At the stage when we group elements because of their sequence of data processing, so that the output of one element is at the same time input of the next one we probably have a brush with sequential cohesion.

There could be only one small issue here. If we have such a sequence, we can cut it in different ways. It means that created classes can do more than one functionality, or quite the opposite – only some part of the functionality. That’s the only reason why in the hierarchy of cohesion types it stands below the last one, which is functional cohesion.

Sequential cohesion – we group elements because of the sequence of data processed by them.

Functional Cohesion

We achieve it when all elements within a class are there because they work together in the best possible way to accomplish the goal – functionality. Each processing element of such a class is its integral part and is critical for the functionality of the class. The class itself performs no less and no more than one functionality.

What’s interesting, mathematical operations are often mentioned as an example of functional cohesion because they do exactly what they should and nothing more than that. I have also mentioned mathematical operations in logical cohesion, but there all operations were grouped in a Utils or Math class, and now I mean more creating a class per operation. So the responsibility of a single class would be only to perform a single operation.

It can be quite easy to judge whether a class has functional cohesion when we deal with a “small” functionality, but at higher levels of abstractions it can become a bit tricky. That’s why we can try to do it by looking for signs of other types of cohesion – similar to the case of data coupling. A bit like a shell game. For that, chapter “Exercises” could be helpful. You will find there quite interesting high-level exercises that can help you with everyday work to define what type of cohesion your class has, so don’t stop, keep reading :).

Functional cohesion – elements within a class are there because they work together in the best possible way to accomplish the goal – functionality.

Nonlinearity

When it comes to types of cohesion, one more thing is worth mentioning. In the book Reliable Software Through Composite Design as well as in Structured Design, the authors suggested assigning a weight to each of those types. Nevertheless, what is most important here they were quite reticent about it. That’s because they insisted that it might be a bit too early for it and they didn’t want it to influence future research into cohesion. They emphasized that values were chosen “based on educated guesses” and “these aspects of the model must be verified and refined based on data collected”.

The only reason why they decided to assign those weights, was to show that in their opinion those weights have nonlinear values and for example, in Structured Design, they were proposed as follows:

0: coincidental

1: logical

3: temporal

5: procedural

7: communicational

9: sequential

10: functional

When we can identify what types of cohesion our classes have, and we can see some places for improvements, these weights can be helpful as they can show us where we should utilize our energy. If we, for example, see that some class has sequential cohesion, it means that it is already almost “ideal” and maybe it makes no sense trying to get to functional cohesion. Instead, it is better to take care of places where the gain is higher, for example, between temporal and procedural cohesion.

Finally, some food for thought

Imagine a situation when without going into any details your colleague tells you “that new class that you’ve just created has poor cohesion.” I can imagine how adrenaline hits you, and at this stage, you probably can react in very different ways. You can flee, fight, or… debate.

Assuming that he or she is not a wild animal, and we are not going to end up as his or her dinner, I encourage you to take the third option. Maybe such a situation is just an opportunity to discuss and define what we understand as cohesion. Maybe we have a different understanding from our colleague. Next, we can also discuss what types of the term we can identify in our system and whether we allow them there or not. It’s not like some of them are right or wrong by default and we should avoid thinking in this way.

If you feel like some “stretching exercises” concerning cohesion, go to Stretch out for your cohesion.

P.S. If you have enjoyed this article and would like to always have it with you offline or you would like to have some super sketch notes with types of coupling and cohesion to print out and stick them above your bed, don’t hesitate and download an ebook, that I have for you. You will find there a few great posts about rotting design, coupling and cohesion put together into PDF, EPUB and MOBI formats.

* indicates required

Six Shades of Coupling

For the history of coupling, how it was born and who is responsible for that as well as why developers usually have problems with understanding it, you should go to A brief history of coupling and cohesion. Here we are going to focus on the concept itself and its types.

Based on our experience, we probably know that coupling is a degree of the relationship between classes – the strength of connections between them. The value of this strength can tell us what a probability of the need for changes in other classes than the class that we are currently working on is. At the same time, it impacts the costs of development and maintenance.

Because of that, we should pursue loose coupling, so one would be able to easily analyze/debug/maintain any class, without detailed knowledge about other classes within the system. The looser the coupling is, the more independent the classes are. How can we define a degree of relationship between the two classes? To answer this question, we should take a look at communication between them. How do they do it? What does the message look like? When we know this, we can define what type of coupling it is and what it entails.

It was Glenford Myers who proposed the most common division of coupling into types in his book Reliable Software Through Composite Design. He defined six types, and now we are going to give them our attention. Let’s go from those considered as the least to those that are the most welcome.

Types of coupling

Content Coupling

Also known as “pathological coupling”.That’s because it usually occurs when one class directly uses private members of the other class. Objects of the first class are changing an internal state and behavior of objects of the second class. From the message’s point of view, it looks like the sender forces the message on the recipient, who is unable to resist.

In our daily dev life, good examples of it could be:

– not respecting access modifiers,

– reflection,

– monkey patching.

The degree of how much we know about the other class is the highest as we know literally everything.

content coupling
Content coupling – pathological – we reach and manipulate the internal state of objects.

Listing 1. The object of the Image probably would prefer to set its description field on its own.

Class<?> clazz = Image.class;
Object imageInstance = clazz.newInstance();
 
Field descriptionField = imageInstance
 .getClass().getDeclaredField("description");
 
descriptionField.setAccessible(true);
descriptionField
 .set(imageInstance, "Pen Pineapple Apple Pen");

Common Coupling

When classes change and communicate via a shared global state, we probably have a brush with common coupling. It means that when the state is changed in runtime, we may have no idea what objects rely on it and how it would impact their behavior.

The name itself is derived from the COMMON statement used in Fortran, where it means a piece of memory shared between different modules. Because of that, they are coupled to each other through this common environment. 

It’s easy to notice that in this kind of coupling, we need to be extremely careful while changing the global state as it could cause changes in places that we would not expect. It requires an immense knowledge of all classes that depend on that state. However, it’s not only about the state but also about the data structure – any change in it causes changes in all dependent classes.

Common coupling – different classes in the system communicate via a global shared state.

External Coupling

Let’s consider now the case when the structure of the message used in communication between classes comes from the outside of our system – the structure is external, so is coupling.

A good example could be using classes from third-party libraries: because of doing so, we become tightly coupled to them, and we lose control of any changes introduced in their next releases. Another example could be some external protocol or data format used in communication.

Listing 2. External coupling – let’s imagine now removing the getAmount() method (that is used everywhere in our system) by an author of popular money class provider library.

import popular.money.class.provider.Money
import popular.money.class.provider.MoneyAmount
 
public class Foo {
 
 public MoneyAmount receive(Money money) {
     //method body
     return money.getAmount();
    }
}

Just to make it clear – we will always need third-party libraries and will always integrate with something outside of our system using some external standard or protocol. However, the most important thing is to disallow those libraries/integrations to spread all around the system. The best idea would be to use them to do their job but then to block them out using our abstractions. The idea behind it is not to “rewrite everything” when, for example, some external library went into a maintenance-only mode, but to change/replace the implementation in some specific places.

External coupling – objects communicate via a protocol or structure that comes from the outside (third-party lib)

Control Coupling

Imagine the situation when you are writing a piece of code and you call a method with some kind of flag as a parameter. Moreover, based on the value of the flag, you can expect different behavior and result. You as a caller have to know quite well what’s going on inside the called method, and the method/class itself is not a black box for you anymore. At this stage, you are more like a coordinator as you say what has to be done and what you expect in return. The described scenario is a typical example of control coupling, where one class passes elements of control to another one. What’s important here, the class that passes those arguments does it deliberately as it wants to achieve specific results. The most common cases for control coupling are methods that take the above-mentioned flags or use switch statements.

In OOP, objects should decide what to do based on their internal state and received data and not on an external flag passed by someone with a view to doing something.

Control coupling – object passes to the other object elements of control that affect execution and a returned result.

Listing 3. Control coupling – it could be a good idea to split this method into two separate methods.

public void save(boolean validationRequired) {
    if (validationRequired) {
       validate();
       store();
    } else {
       store();
    }
}

Stamp Coupling and Data Coupling

When communication between classes is not either pathological (content coupling) or through the shared global state (common coupling), neither via external protocol nor structure (external coupling) and it has no elements of control (control coupling), then we have probably ended up with one of the two types that put the message and its structure on the pedestal.

In the first type (stamp coupling), classes use data structure (our own and not from the third-part lib) in communication. Usually, it is some kind of DTO. The recipient can decide whether he wants to use all data from the structure or just a part of it because the rest can be totally useless in a specific context.

For example, in different applications, we can often find classes whose name ends in “details” or “data”. Does it resonate with you? So let’s take a look at EmployeeDetails class, as you probably suspect you could find everything about an employee there – the sky is the limit. Let’s assume now that we have the method that takes an object of this class as a parameter and should return an employee’s address, based only on the employee’s id from inside of the whole structure. Now we could argue that there is no coupling between caller and recipient as we pass some set of data, the recipient takes what it needs, and that’s it. However, there is coupling to the whole structure of the passed data. There could be a case when a change occurs in a part of the structure that is not used by the recipient, but some adjustments are still required.

This kind of coupling can also lead to the creation of artificial data structures, that would hold unrelated data. Then frivolous adding different items to such data “bags” could become a common practice in our code.

Let’s think now how to avoid coupling to the whole data structure. The answer is quite clear – use just data items instead. Doing so, we should end up with the loosest type of coupling that is data coupling.

Basically, it means that the recipient (method) takes a list of arguments (values). Notice that here we only pass values that are key to method execution. In other words, there is no space for any unnecessary items. For example, instead of passing the whole EmployeeDetails structure, we could pass just an employee’s id. In return, we also could get some single value, like a zip code.

Listing 4. Difference between stamp coupling and data coupling.

//Stamp coupling
public EmployeeAddress findAddressFor(EmployeeData employeeData) {
 
 EmployeeAddress address = repository
 .findByEmployeeId(employeeData.getId())
 //method body
 return address;
}
 
//Data coupling
public ZipCode findZipCodeFor(EmployeeId employeeId) {
 
 EmployeeAddress address = repository
 .findByEmployeeId(employeeId)
 //method body
 return address.getZipCode();
}

We can say that a class/method is marked by data coupling when we can’t notice any other types of coupling there. Why did I even mention it? Let’s imagine a situation when we see a method that has a single parameter. We could potentially think that there is a stamp or data coupling, but it can turn out that actually the parameter is an element of control that is specific for control coupling. In such a situation, we should take a closer look at how a caller of the method sees it. If the caller uses a parameter as an element of control and expects some specific behavior by setting it then indeed we have a case of control coupling, however, if the caller passes the value and treats it just like any other data we are probably dealing with data coupling.

Communication in stamp coupling (on the left) and data coupling (on the right)

That’s it when it comes to types of coupling. I want to repeat here that this division into types is the original by Myers – the emphasis is put on communication between classes and the message itself. I’m mentioning it as there are also other types of coupling that are specific for OOP and those are probably the ones that we are most familiar with. What I mean by that is how classes are connected physically. Whether we create an object of some class directly, or  use an interface, or, finally, if we may just emit some events without the knowledge who and how consumes them.

Myers defined types of coupling when procedural programming reigned supreme. I have an impression that then when OOP became popular, those types lost popularity and everybody’s attention focused only on those specific for OOP. Why do I think so? When we take a look at most code examples on the Internet looking for “examples of tight/loose coupling”, we mainly find code snippets that consider it only on a level of using interface (loose coupling) vs. specific implementation (tight coupling). But that’s a totally different story for another discussion or post ;).

Decoupling

Now that we know what types of coupling we can expect and how to identify them in our code, it would be good to find some ways of getting those types that are usually more desired. To do that we can use decoupling, which can be any technique that helps us to achieve more independent classes.

Each of the types suggests some forms of decoupling and in general it means favoring types with looser coupling. For example, if we identify a type of control coupling in a method and we can see two flows with different results based on the passed element of control, maybe we could split the method into two separate methods. Another example could be a situation when we see that a method has a parameter that is data structure and uses just a few fields from it. In this case, maybe we could replace the whole structure with a short list of parameters.

The next technique could be to design classes in such a way as though communication between them could be done using queues. In this approach, we focus on what a class should do and what it needs to execute its logic. Moreover, the moment of execution becomes irrelevant.

A good practice could also be to use “local” (in other words, context-specific) data structure in communication between classes. Thanks to that, we don’t allow them to spread all around the system but just to be used where they fit.

So that would be it for now when it comes to coupling. Last but not least I would like you to remember there is no black or white here. As it is quite common in our profession – it all depends – usually on the case or context. Sometimes we will need strong coupling and trying to lose it would not make any sense. The most important thing for us is knowing all those types and knowing the good, the bad, and the ugly sides to them. 

Now it’s time to move on and take a closer look at cohesion if you haven’t done it yet.  

P.S. If you have enjoyed this article and would like to always have it with you offline or you would like to have some super sketch notes with types of coupling and cohesion to print out and stick them above your bed, don’t hesitate and download an ebook, that I have for you. You will find there a few great posts about rotting design, coupling and cohesion put together into PDF, EPUB and MOBI formats.

* indicates required

A brief history of coupling and cohesion

From the very first time when coupling and cohesion were defined, there was one issue with both of them. Let me ask you a few questions:

– how can I recognize that one class is tightly coupled with another?

– how can I verify whether or not a specific class is cohesive?

Well, my class is definitely highly cohesive and loosely coupled with others, but a class of my colleague’s… hmmm… he or she had better reconsider their future career as a developer.

It turns out that each of us can have different images of what is loosely/tightly coupled and what is or is not cohesive. What is most interesting, any of us can be right. Why is that so? Both concepts are highly abstract. How do we deal as human beings with such concepts? We look for analogies to our daily lives. You can quickly validate this by looking into the most highly rated answers to general questions about coupling and cohesion on Stack Overflow. You will find a ton of analogies. Just take a look:

“Car and Spare parts analogy of coupling”

“You and the guy at the convenience store.” – loose coupling

– “The cheese store.” – high cohesion

– “You and your wife.” – tight coupling

Analogies are great, and we like them. They help us to get the context and shed some more light on the problem. But do they really explain everything? I don’t think so. Ok, so now it’s time to dig into Wikipedia or any other pedia. What we see there are some classical definitions that I more or less included earlier in this article. Then we scroll a bit down, and we notice something more as there are types of coupling and types of cohesion. No one told us about them, so let’s take a look at what more your search engine of choice returned. So now is the moment when real fun begins. There are a lot of publications and books. One author says that, for example, there are only two types of coupling, another says there are twelve. Someone else that all the others are wrong because they didn’t consider this and that. At the same time, some of the publications even use different names; for example, cohesion is sometimes referred to as “functional binding” or “strength”. 

When did it all begin?

To answer this question, we need to go back in time to 1974, when Larry Constantine, Glenford Myers, and Wayne Stevens published in IBM Systems Journal their article called Structured Design. They described coupling and cohesion there, but actually, Larry Constantine is considered to be the father of these concepts.

One year later, the book Reliable Software Through Composite Design by Glenford Myers was published. Quite a big part of it was dedicated to both concepts. On a side note, I can say that it’s a great book; it’s written in an accessible way, and the most fascinating thing about it is that while reading it you can have an impression that in the last 50 years little has actually changed when it comes to the challenges in software design and its modularity.

Then after another few years, Larry Constantine and Edward Yourdon published their book called Structured Design – the same as the article from 1974. They were also referencing Mayers’ book, which goes to show that those guys for few years were doing a lot of research in the area of coupling and cohesion.

We could probably say now that those were the 70s, so it should be quite outdated now. Procedural programming was standard back then, they had modules, and now we do OOP, we have our cool, shiny classes. Why should we even care?  Well, it turns out that even though paradigms have changed, coupling as well as cohesion are still with us and they feel pretty fine in OOP. When in the early 90s Timothy Budd published his book An Introduction to Object-Oriented Programming you could find there that counterparts of modules in OOP are classes. Personally, I would go further and say that both concepts are so universal, that I can easily imagine, for example, a loose/tight coupling between packages in Java or even between microservices.

I hope that I have sparked off your curiosity and you would like to know more about both concepts as well as their above-mentioned types :). Don’t wait and go here to read more about coupling or here about cohesion.

P.S. If you have enjoyed this article and would like to always have it with you offline or you would like to have some super sketch notes with types of coupling and cohesion to print out and stick them above your bed, don’t hesitate and download an ebook, that I have for you. You will find there a few great posts about rotting design, coupling and cohesion put together into PDF, EPUB and MOBI formats.

* indicates required