Buildstuff Conference Report - day 2
Just like yesterday I was unlucky with my talk selection in the morning. I picked some talks that were presented with less energy/focus, or that contained less/no useful suggestions. The afternoon made up for it though.
Teaching your Team CQRS/ES 2.0
Chris Condron @CLCondron
I found the concept behind this talk a very interesting one: how do you teach your team CQRS and event sourcing? Chris first mentions several possible reactions to this new way of doing things (assuming you have previously been doing just regular Active Record or Data Mapper persistence, with the write model being the same as the read model). Chris has found that CQRS/ES is hard to grasp at first, hard to get started with, but once you’re past the startup phase, it doesn’t get much harder and you can be more effective for a long time. In fact, it will be harder to keep a system under control that doesn’t use CQRS/ES, when writing and reading is entangled and it’s hard to figure out what’s going on and how to make isolated changes.
While teaching CQRS/ES Chris finds that developers move from a procedural style of programming to a more compositional style. They move from conditional code to functional code. And their object design evolves from being stateful to being stateless. He finds that showing code in the new style helps getting the ideas across and also helps developers to adopt a new, different way of thinking.
As part of a training phase, Chris lets developers work on a programming assignment about account balancing. Posing gradually more difficult problems, developers will learn that event sourcing is a great, fast and scalable way to get different perspectives on data.
Chris encourages developers to express use cases of an application as streams of messages. What information is needed to fulfil some request? What’s needed in the next stage? What will be passed along? What will be persisted? But also: at what stage can we prevent problems from happening in later stages?
CQRS/ES requires people to learn many things (which will also make them better at OO programming in general). But once you know all of it, you will find that it becomes easier to make isolated changes to the application.
Chris mentions that when application design starts to revolve around internal messaging, external messaging becomes important too. Accepting and emitting messages allows you to solve old problems in new ways. But this requires developers to learn about messaging integration patterns. He has another assignment for this, about a restaurant application. Communication between waiters, the cashier, the cook and the visitors of a restaurant should happen based on domain events, which are messages travelling asynchronously between processes.
Some final suggestions about CQRS/ES systems: if refactoring is a lot of work, this is a sign that the system needs fixing. It shouldn’t be hard to work with CQRS/ES, it should mainly bring you speed and better solutions. So, if you see complexity, fix it; don’t wait.
Functional Architecture: The Pits of Success
Mark Seemann @ploeh
Mark starts with describing a very relatable conclusion about object-oriented programming: it requires a lot of work to get it right. It takes years of learning (and teaching others) to accomplish good, maintainable software. And yet, high quality OO code doesn’t reach an equilibrium. Anyone can come around (or you can leave the project) and gone are all the good practices. The main idea behind this talk is to show how functional programming (FP) will naturally lead to code that follows best practices, which would take a lot of work when doing OOP (mainly because it’s impossible to do something else!). Also, FP allows you to enjoy good quality code without the fear of someone deteriorating the code by violating some dependency rule. Or as Mark puts it: you can go on vacation and get back to a code base that’s still in a good state. This is why he considers FP to have several “pits of success” to which you’ll naturally gravitate, as opposed to OO with its mountain tops of hard to achieve success.
For example, as explained in a blog post by Mark I had read earlier - FP has no need for explicit Ports & Adapters. Simply because with FP you distinguish pure from impure functions, you automatically get this “ring” of outer functions that are concerned with “the world outside”. These functions are non-deterministic, or they have side effects. The Haskell compiler will even prevent pure functions from calling impure ones, in the same way that for example a Clean architecture prescribes that dependencies in a layered application may only point inwards, never outwards. Except that Clean architecture is OO and you don’t get any of this kind of support from the compiler. With OO it all comes down to proper education and discipline. With FP this isn’t even required - you simply can’t get this wrong.
Another topic Mark discusses is the universal struggle to get object design itself right. OO programmers always have to be wondering: should this method be on this object, or should it be a dedicated service itself? We’re trading off extreme encapsulation on the one hand and feature envy on the other hand.
With FP it’s clear: there’s data and there’s functions. You can’t combine the two into objects. You can still have rich data types, which look like objects, but have no behavior to them.
Finally, making code unit-testable in OO is a lot of work. To test a unit of OO code you have to make all dependencies injectable. If you don’t do this, you won’t reach the goal of isolation and that will leave you with non-deterministic unit tests. Dependency injection (DI) is itself a very important technique, but it takes a lot of boiler-plate code, both inside a class as well as in the setup code for its unit test. This means unit testing OO code comes with a high cost (which leads some people to dismiss DI and/or unit testing completely). With FP you only have functions. If a function is pure, testing it is almost trivial, since it will be completely isolated by nature; if it would be non-deterministic, it couldn’t be pure after all. Of course a function may need some other function to do some more work, but it would be possible to provide this supporting function itself as an argument. This gives you the option to swap out behavior in a unit test with something like a “stub function”.
There are more ways in which FP helps you end up with a better design without doing much effort (even though it will be quite a big change in mindset). I can recommend to follow along with what Mark is posting on his blog to learn more about this.