Hi after a long time! This time I’d like to present to you my short story about creating a side-project with my friends. Yep, I know that the title is a little click-baiting, but I want you to read this post.
Let’s start from the beginning - as I’ve mentioned I’m working on a side-project that is a SaaS application for car rental companies. I know, I know - it’s not rocket science, but we’re trying to do the best experience both for our customers and… for us. We want to have an amazing developer experience level and ease of introducing new engineers to the project in the future. I’m nearly sure you have a graveyard of side-projects in your GitHub repositories - you’re not alone. Most programmers can’t finish projects because of over-engineering, over-thinking, over-…doing fancy stuff like tactical DDD, BDD, TDD, XDD instead of GETTING F—ING SHIT DONE. I was the same - I was doing an amazing architecture, clear, beauty code… but! The projects went away because I was exhausted from doing everything in a purist way. This time is different, seriously!
We’ve started the project wanting to deliver an amazing product and also still learning some things meanwhile. It wasn’t that easy because every step in the wrong direction may cause over-engineering. Okay, I’ve blamed DDD for some fails and yes, I keep this opinion, but it’s related to tactical DDD - not strategic. The second one is brilliant, amazing, and my lovely approach to software engineering. So… yes, we did deep modeling of our domain, and we just wanted to understand how the business should work in the best possible way. Probably you get me now like an idiot that ignores amazing and powerful tools… but I am not. I love them but not when I need to deliver a new startup-like system.
Ah, yea - let’s go back to the main topic. We did an event storming, domain modeling, etc. just to understand as much as we can the business model of our system. We didn’t write any line of code yet, we just talked and moved some stickers on a virtual board. Then after all we’ve discovered our domains - core, supporting, and domain. If you’ve ever heard about DD you probably know something about them, but we did it in a practice with a goal. The goal was to understand which modules should be created with an overengineering back of the head. If something is not our core - then it’s not recommended to waste the time doing too much detailed application architecture or something in this place.
Let’s meet the prom queen of our application. It’s so hot topic nowadays at the conferences - to be honest, also I did two or three presentations about it some time ago. The queen is a modular monolith. This time the most important are boundaries between the modules. We’ve established one rule: you can not use anything between the modules - the walls are like a Gandalf saying “YOU SHALL NOT PASS!”. And yes, we did it. Our modular monolith has solid walls between the business blocks. Every block represents our domain language. Vehicle Catalog, Fleet Management, RentalUnits, Reservations, Vehicle Maintenance, Rentals, and so on. Do you feel the climate of a car rental company? I hope so!
Another rule that we’ve established is that - we’re not doing cross-context queries and commands. Everything should be event-driven. Sounds like a complex solution but to be honest it’s not. If you need some information - just subscribe to a specific domain event and for example, denormalize information. There’s nothing wrong when many parts of the application know something about… let’s say a vehicle. In a Vehicle Catalog that’s the set of specific make, model, variant, etc but in Fleet Management, it’s related to car’s plates, mileage. In an availability context (yes, we have it xd) it’s just a subject that you can check if in the specific period it’s free to take.
Okay, but let’s talk about the trade-offs. In a pure Domain-Driven Design solution using tactical tools - you should create a pure model in the Domain layer that is not extending nor using anything from the outside. This model should be used only via an application layer that’s not leaking any details to the higher levels like a user interface. Bla, bla - ye that’s the way how we do it in the core-domain modules but we don’t care about it in generic/supporting ones. Let’s meet the Vehicle Catalog module - it’s our business context that represents all the existing makes, models, variants, and their data - that’s like EURO TAX. As you may know - it’s just supporting one and… to be honest we can handle importing from the CSV file, inserting it into the database in one single file, and… we’re okay with this solution! Why? Because we won’t make money on inserting and publishing information about the cars, it’s not our business model. The same we are doing with the UI layers. Yes, in a clear way we should only dispatch commands and queries from this place but… once again - we don’t care if it’s only a supporting domain. This project is created using API Platform - that’s the tool for bootstrapping fully operational API-first applications and let us create full features using just a few notations. THAT’S OKAY, don’t worry. And yes - our entities contain infrastructure annotations like ORM mapping or API Platform definitions and one more time - that is fine. Of course - my software craftsmanship’s heart bleeding when I see it but… I want to GET SHIT DONE not to create another dead shrine to clean code.
If you’re still here - then there are two possibilities. The first one - you got me as an asshole after a weekly Bootcamp in a Pcim (that’s a small city in Poland) who doesn’t know about software development OR… you got me as a crazy man. Anyway, let’s keep talking about software engineering and my side-project. The next big thing: events. Domain events are the most important thing in the whole project. Why? Because they hide implementation, hide my ignorance, hide low-quality of non-important modules. Let’s go back to the Vehicle Catalog context. As you may know - we’re doing a data import here, and also we’re exposing a few endpoints just for the clients (front applications). There is one more thing - we’re doing a translation of this piece of shitty code into a nice, simple domain event. What is a full entity of a vehicle? The aggregate contains make, model, and variant like: Porsche 911 964 Targa. That’s one, specific vehicle. If we’ve imported this one - we’re emitting an event: “Vehicle Created” that contains only basic information about this car, no more. Then the event is consumed by many different contexts - for example, Fleet Management consumes this event and creates duplication of needed data - thanks to that we can use FleetManagement endpoints for example to present available cars in the fleet without calling Vehicle Catalog for details like name of the car’s model. I think that’s a huge power gain! We don’t have any coupling between modules and we’re so proud about that!
What if we would like to do a refactoring of some CRUD-like module that is currently implemented as a small ball of mud? That’s easy because we’re doing a refactor inside the module and still keeps communication without any change - we’ll use the same events as before. That’s beautiful.
Okay, now - think about your side-project. Don’t you think doing a DDD, BDD, TDD, Hype-Driven Development is not just a muda in many places of your system? Wouldn’t it be easier to create solid walls between business boundaries and hide the implementation behind them? Think about Pareto Principle and about lean. You don’t need to over-think the project to create something good that gives your clients true value. Create a model of your business - what will earn money and give a value and then - build the best possible model here but not better. Perfectionism is not your goal - your goal is to bring an amazing product to the people who need it.
Software Engineering is not about creating shrines of clean code that would make Uncle Bob happier. For me - the best engineer should be described as a Product Engineer; not PHP Engineer nor C# engineer and even not a WordPress 5.7.1 Developer (xd). Take shortcuts where the road is flat (supporting domain) and climb with the best tools ever when you’re fighting about your business (core domain). Zero waste software development? Yep, I’d name it like that. Essentialism is crucial.