top | item 40925128

(no title)

tomck | 1 year ago

Every layer you create is another public API that someone else can use in some other code. Each time your public API is used in a different place, it gathers different invariants - 'this function should be fast', 'this function should never error', 'this function shouldn't contact the database', etc. More invariants = more stuff broken when you change the layer.

So let's say you have some 'User' ORM entity for a food app. Each user has a favourite food and food preferences. You have a function `List<User> getListOfUsersWithFoodPreferences(FoodPreference preference)` which queries another service for users with a given food preference.

The `User` entity has a `String getName()` and `String getFavouriteFood()` methods, cool

Some other team builds some UI on top of that, which takes a list of users and displays their names and their favourite food.

Another team in your org uses the same API call to get a list of users with the same food prefs as you, so they loop over all your food prefs + call the function multiple times.

Amazing, we've layered the system and reused it twice!

Now, the database needs to change, because users can have multiple favourite foods, so the database gets restructured and favourite foods are now more expensive to query - they're not just in the same table row anymore.

As a result, `getListOfUsersWithFoodPreferences` runs a bit slower, because the favourite food query is more expensive.

This is fine for the UI, but the other team using this function to loop over all your food prefs now have their system running 4x slower! They didn't even need the user's favourite food!

If we're lucky that team gets time to investigate the performance regression, and we end up with another function `getListOfUsersWithFoodPreferencesWithoutFavouriteFoods`. Nice.

The onion layer limited the 'blast radius' of the DB change, but only in the API - the performance of the layer changed, and that broke another team.

discuss

order

djeastm|1 year ago

This is where command/query separation is strongest regardless of onion/layered architecture. Your queries/reads are treated entirely separately from your commands/writes so you're free to include/exclude any of the joined data a particular query doesn't need.

tomck|1 year ago

I have no idea what you're talking about, my example doesn't include any writes, only a read