We did two meetings this week talking about our project’s architecture, what it currently looks like and why and where we want to go, mainly because most of us are new to this project. Among us backend devs, there is one experienced Ruby dev (Sascha, the one I did my last team swap with) who has been in this team for almost a year, one dev with lots of experience with JS frameworks but not Ruby who has been there for a few months, another experienced Ruby dev who has joined Studitemps a day before I joined this team and me who doesn’t really know how much Ruby knowledge she even has, yet.
The project itself consists of a semi-legacy project (that I even used to work on, but as a non-coding tester) that lived on its own from 2016 to 2018, and lots of new code and ideas around and next to this old thingy. This means that there is a mix of concepts and structure in it, but with a general idea on how to move the refactoring process along.
In the first meeting, we mainly talked about the domain-driven aspect of the code. Sascha explained how we are differing “from the book” (mainly Domain-Driven Rails which I have yet to read) by using Command Handlers to handle the business logic instead of Application Services and rich Domain Models. A Command Handler’s input is always a Command1 and its output is (almost) always a Domain Event. Some of these events get published to RabbitMQ so they can be consumed by other applications, some are just internal events (but ready to be published should someone need them in the future). In general, there should be a 1:1 relationship between Commands and Command Handlers and we2 are mostly following this rule. Commands only contain strictly typed (and usually only scalar) data, so usually the URI of an object and not the object itself. If an object’s state needs to be changed due to the command, it’s the Command Handler’s responsibility to load and update the object from the corresponding repository. This means that a command is usually very similar to the event that is thrown as the result of the Command Handler’s work and easy to serialise. As mentioned in a previous post when I spent a few days in this team, there are helper classes for Commands, Handlers, Events etc. that take care of the boilerplate stuff that has to be done for this flow to work.
In general, all actions should trigger a command, be it user input that goes through the controller or external stuff from APIs or consuming events. Validation and transformation is done before the command is written (e.g. in Form Objects for user input) so the caller is responsible for making sure the action is suitable for triggering a command (e.g. when something is edited and saved without actually changing anything, there should not be a command in most cases). There are exceptions when a Command Handler itself checks some Policies and then returns either an Exception. But mostly, Command Handlers are responsible for typecasting and not validating. CommandForm uses ReForm to build the command and throw it at the Command Bus3. The Command Handler does everthing in one transaction until the external event is written. Only the publishing to RabbitMQ part is handled outside of the transaction so that the internal state of the application is not dependent on an external service. The Action -> Command -> Command Handler -> Event flow is the main part of the application.
In the second meeting, we got more into details in terms of how we want to write this code from now on. One of the other guys had a question about Decorators and then there was a long conversation between the two experienced Ruby devs about Decorators and Presenters and View Models that went completely over my head. There’s just bullet point like “Hanami Presenter”, “Daper Decorator”, “read about Decorator Pattern?” in my notebook. Something to either learn later or just look at some code to figure out what does what, I guess. Then we talked a bit more about the way Commands and Command Handlers are written, how and when we are more object-oriented vs. functional, the naming of things (should reflect business logic and not generic/technical stuff) and in the end, we looked at some things that differ from the way we want to do things because they need to be there for legacy stuff to work. And at the very end, we talked about registries and contexts and how we try not to use e.g. repos from other contexts so that things stay modular and are possibly able to be divided later on. My last note is “irgendwas mit ableiten oder so?” and I have only a very vague idea what part of the code this is even refering to. 90 minutes of talking about technical details that are way beyond my experience level is a little bit exhausting.
But all in all, these two meetings were great! I understood a lot more about the structure of this application, I realised how much I already know about Domain-Driven Design and the way we do event-based systems at Studitemps from what I did in the past 2 years and I have a rough idea of things I should look up or ask the others about. I guess I joined this team at the perfect time. I often know too little to understand which questions I need to ask and I’m often too anxious to just yell for help, so it’s great that there’s someone else who is new to this team but experienced enough to initiate a conversation that helps me understand things better.
But for now, I’m off work and on my way to SoCraTes. A good time to let all the input from the past few days sink in and get some new and different ideas. I plan on picking as many brains as possible about structuring learning and I plan on overhauling my “apprenticeship” project with some input from the conference and some thinking while being on trains once I’m back. Exciting times ahead.
- I took pages of notes during these meetings and there is one that says “command, not params like Trailblazer Operations”. I know from working on the legacy system that we used Trailblazer for a bit and that the team has since decided to move more into a Domain-Driven Design way of structuring their application, but for me, that’s mostly words right now. And I’m not quite sure if I should try to understand what they mean because I guess we’re not doing things the Trailblazer way from now on. So should I look this up? Maybe later, I guess. [return]
- Still weird. I struggle with “we” and “they” when talking about things my current team does vs. things my old team does/did. Saying “we” when I’m talking about the concepts of this application that have been worked out without me being involved at all feels strange, but saying “them” feels like I’m an outsider observing instead of a team member involved in the project and its architecture from now on. [return]
- We are getting to the stage where I’m just typing down what I have written without it having any meaning to me. That’s one thing where this blogging thing comes in handy: I realise that I have understood even less than I thought and know where to ask for help again. [return]