[HN Gopher] Why Use Onion Layering?
       ___________________________________________________________________
        
       Why Use Onion Layering?
        
       Author : thunderbong
       Score  : 64 points
       Date   : 2024-07-07 16:44 UTC (3 days ago)
        
 (HTM) web link (garrettdbates.com)
 (TXT) w3m dump (garrettdbates.com)
        
       | theamk wrote:
       | Reads like a middle part of the post... What exactly does author
       | mean by "onion layering" and what are they being opposed to?
       | 
       | The author supplies some examples (new API field, new
       | microservice dependency, new library dependency) but their fixes
       | seem to apply to any well-organized code - I don't see why you'd
       | need to use some mysterious "onion layering" to create a wrapper
       | around insane dependency and isolate it from the rest of the
       | code.
       | 
       | (Google searches show only a few results and they all seem
       | slightly different, so I am guessing this variant of "onion
       | layering" is something author made up but didn't share with
       | anyone - their blog has only one post)
        
         | 8note wrote:
         | I think you're looking for
         | https://jeffreypalermo.com/2008/07/the-onion-architecture-pa...
        
           | theamk wrote:
           | Except your link has layers as "(Infra / UI / Tests) ->
           | application -> domain services -> domain model", with "core"
           | cross-cutting via inner 3 layers
           | 
           | While OP has "config -> (API / Infra) -> core"
           | 
           | Seems pretty different... I don't think author was referring
           | to that particular page.
        
         | cjohnson318 wrote:
         | Yeah, I was confused. This is basically just, "have well
         | defined interfaces, and pass simple data through them", but
         | with no concrete examples.
        
       | DrMiaow wrote:
       | Isn't this how everyone (competent) designs systems?
        
         | efnx wrote:
         | You would hope, but some folks don't have a background in APIs
         | and don't really know when life could be better.
        
           | barbariangrunge wrote:
           | What do you mean by background in apis? You mean experience
           | working with them?
        
         | Sankozi wrote:
         | No, you do not need to design system like that. Just because
         | there is a chance that something might change (domain logic,
         | library/REST API), does not mean you need to create anti
         | corruption layers everywhere. They limit problems during the
         | (possible but not certain) change but they make code less
         | readable, less performant and harder to test.
         | 
         | Always analyze and decide if it is worth it.
        
         | whstl wrote:
         | I feel like systems design is a bit like the Anna Karenina
         | quote, every good software is alike, but the bad ones are
         | different in their own way.
         | 
         | The Gary Bernhardt talk "Boundaries" shows an end result that
         | is very close to The Onion Architecture presented here. And
         | Onion is of course very close to the also popular Clean
         | Architecture and Hexagonal Architecture. Which at the end are
         | very close to applications built using the principles that
         | cjohnson318 mentioned: "have well defined interfaces, and pass
         | simple data through them".
         | 
         | This is all very close to some of the principles Bertrand Meyer
         | teaches. For example, having different modules that make
         | decisions and different modules that perform actions. Which is
         | close to Event Sourcing and CQRS. Which once again is close to
         | BASIC having SUBs and FUNs.
         | 
         | Sure, under a microscope you will have different terminologies,
         | and even apply different techniques and patterns, but the
         | principles in the end are very similar. You might not have
         | anti-corruption layers anywhere, as the sibling commenter
         | mentioned, but that's missing the forest for the trees: the end
         | goal and end result are virtually the same, even if the
         | implementation is different.
         | 
         | In the end happy families have different socioeconomic
         | backgrounds, different ethnicities and religions, but they're
         | still alike. It's the bad ones that have lots of special cases
         | and exceptions everywhere in their design or whatever it is.
        
         | tomck wrote:
         | No, this is how everyone incompetent designs systems
         | 
         | Layers of generic APIs required to be 1000x more complex than
         | would be required if they were just coupled to the layer above
         | 
         | Changing requirements means tunneling data through many layers
         | 
         | Layers are generic, which means either you tightly couple your
         | APIs for the above-layer's use case, or your API will limit the
         | performance of your system
         | 
         | Everyone who _thinks_ they can design systems does it this way,
         | then they end up managing a system that runs 10x slower than it
         | should + complaining about managers changing requirements  'at
         | the last minute'
        
           | vbezhenar wrote:
           | I don't follow your reasoning.
           | 
           | The point of abstraction is to limit blast radius of
           | requirement changes.
           | 
           | Someone decides to rename field in API? You don't need to
           | change your database schema and 100500 microservices on top
           | of it. You just change DTO object and keep old name in the
           | other places. May be you'll change old name some day, but you
           | can do it in small steps.
           | 
           | If your layer repeats another layer, why is it a layer in the
           | first place? The point of layer is to introduce abstraction
           | and redirection. There's cost and there's gain.
           | 
           | Every problem can be solved by introducing another layer of
           | indirection. Except the problem of having too many layers of
           | indirection.
        
             | tomck wrote:
             | 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.
        
               | djeastm wrote:
               | 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 wrote:
               | I have no idea what you're talking about, my example
               | doesn't include any writes, only a read
        
               | djeastm wrote:
               | Forgive me for not tying it back to your example
               | explicitly.
               | 
               | Your example was a read. So in that case since there's no
               | change in state (no need for protection of the
               | data/invariants) there's no dangers in having different
               | clients read the User records from the datastore however
               | makes sense for them. They could use the ORM or hit the
               | DB directly or anything, really. So
               | getListOfUsersWithFoodPreferences and
               | getListOfUsersWithFoodPreferencesWithoutFavouriteFoods
               | living together as client-specific methods is absolutely
               | fine. It's only when state changes that you need to bring
               | in the User Entity that has all of the domain rules and
               | restrictions.
               | 
               | The idea is that while on Commands (writes) you need your
               | User entity, but on Queries (reads) there's no need to
               | treat the User data as a one-size-fits-all User object
               | that must be hydrated in the same way by all clients.
        
               | tomck wrote:
               | > So getListOfUsersWithFoodPreferences and
               | getListOfUsersWithFoodPreferencesWithoutFavouriteFoods
               | living together as client-specific methods is absolutely
               | fine
               | 
               | Sorry; my point was that adding this function as a public
               | API 'onion layer' in your code means you're less able to
               | adapt to change. The fact this function returns a `User`
               | entity isn't particularly important - it's the fact when
               | you make a function public, other teams will reuse your
               | function and add invariants you didn't realise existed,
               | so that changing your function in the future will break
               | other teams' code.
               | 
               | Less public 'onion layers' means less of this
        
             | usrbinbash wrote:
             | > The point of abstraction is to limit blast radius of
             | requirement changes.
             | 
             | No, the point of abstraction is to make things easier to
             | handle.
             | 
             | At least that is the _original_ meaning of the term, before
             | the OOP ideology got its hands on it. A biology textbook
             | talks about organs before it talks about tissues before it
             | talks about cells before it talks about enzymes. That is
             | the meaning of abstraction: Simple interface to a complex
             | implementation.
             | 
             | In OOP-World however, "abstraction", for some reason,
             | denotes something _MORE COMPLEX_ than the things that are
             | abstracted. It 's a kind of logic-flow-routing-layer
             | between the actually useful components that implement the
             | actual business logic.
             | 
             | And such middleware is perfectly fine ... _as long as it is
             | required_. Usually it isn 't, which is where YAGNI comes
             | from.
             | 
             | Now, pointless abstractions are bad enough. But things get
             | REALLY bad, when we drag things that should sit together in
             | the same component, kicking and screaming, into yet another
             | abstraction, so we can maybe, someday, but really never
             | going to happen, do something like rename or add a field to
             | a component. Because now we don't even have useful
             | components any more, we have abstractions, which make up
             | components, and seeing where a component starts and ends,
             | becomes a non-trivial task.
             | 
             | In theory this all seems amazing, sure. It's flexible, it's
             | OOP, it is correct according to all kinds of books written
             | by very smart people.
             | 
             | In reality however, these abstractions introduce a cost,
             | and I am not even talking about performance here, I am
             | talkig about readability and maintainability. And as it
             | turns out in the majority of usecases, these costs far
             | outweigh any gains from applying this methodology. Again:
             | There is a reason YAGNI became a thing.
             | 
             | As someone who had the dubious pleasure to bring several
             | legacy Java services into the 21st century, usually what
             | following these principles dogmatically results in, is a
             | huge, bloated, unreadable codebase, where business
             | functionality is nearly impossible to locate, and so are
             | types that actually represent business objects. Because
             | things that could be handled in 2 functions and a struct
             | that are tightly coupled (which is okay, because they
             | represent one unit of business logic anyway), are instead
             | spread out between 24 different types in as many files. And
             | not only does this make the code slow and needlessly hard
             | to maintain, it also makes it brittle. Because when I
             | change the wrong Base-Type, the whole oh-so-very-elegant
             | pile of abstractions suddenly comes crashing down like a
             | house of cards.
             | 
             | When "where does X happen" stops being answerable with a
             | simple `grep` over the codebase, things have taken a wrong
             | turn.
        
             | seanhunter wrote:
             | > The point of abstraction is to limit blast radius of
             | requirement changes.
             | 
             | The problem is in many/most? systems there's no way it can
             | possibly do this, because the abstraction that looked like
             | a perfect fit for requirements set 1 can't know what the
             | requirements in set 2 look like. So in my experience what
             | ends up happening with the abstraction thing is people put
             | all sorts of abstractions all over the place that seem like
             | a good idea and when requirements set #2, #3, etc come
             | along you end up having to change all the actual code to
             | meet the requirements _and_ all of the abstraction layers
             | which no longer fit.
             | 
             | To choose a couple of many examples from my personal
             | experience:
             | 
             | - One place I worked had a system the author thought was
             | very elegant which used virtual functions to do everything.
             | "When we need to extend it we can just add a new set of
             | classes which implement this interface and it will Just
             | Work". Except when the new requirements came in we now
             | needed to dispatch based on the type of two things, not
             | just one. Although you can do this type of thing in lisp
             | and haskell you can't in C++ which is what we were using.
             | So the whole abstraction ediface cost us extra to build in
             | the first place, performance while in use and extra to tear
             | down and rewrite when the actual requirements changed
             | 
             | - One place I worked allowed people to extend the system by
             | implementing a particular java interface to make plugins.
             | Client went nuts developing 300+ of these. When the
             | requirements changed it was clear we needed to change this
             | interface in a way a straight automated refactor just
             | couldn't achieve. Cue me having to rewrite 300+ plugins
             | from InterfaceWhichIsDefinitelyNeverGoingToChangeA format
             | to InterfaceWhichIsHonestlyISwearThisTimeAbsolutelyNeverGoi
             | ngToChangeB format. I was really happy with all the time
             | this abstraction was saving me while doing so.
             | 
             | Most of the time abstraction doesn't save you time. It may
             | save you cognitive overload by making certain parts of the
             | system simpler to reason about, and that can be a valid
             | reason to do it, but multiple layers is almost never worth
             | it and the idea that you can somehow see the future and
             | know the right abstraction to prevent future pain is
             | delusional unless the problem space is really really well
             | known and understood, which is almost never the case in my
             | experience.
        
           | redman25 wrote:
           | I've experienced the same. It's difficult for frontend and
           | backend to communicate because there's a "translation layer"
           | in between. Shipping a new feature is 100x harder than it
           | needs to be because everything has to be translated between
           | two different paradigms.
        
         | Quothling wrote:
         | It was 10-20 years ago. Today nobody competent does it because
         | it doesn't scale. A lot of the good parts of onion layering
         | still exists in more modern architectures, especially in
         | languages which are still very tied to the original OOP
         | academic principles like Java or C# where you're likely to see
         | interfaces for every class implementation, but as time has
         | moved forward it's not really necessary to organize your
         | functions inside classes, even if you're still doing heavy OOP.
         | So today you're more likely to see the good parts of the onion
         | layering build into how you might do domain based architecture.
         | So even if you're doing models, services and so on, you build
         | them related to a specific business domain where they live
         | fully isolated from any other domain. Which goes against things
         | like DRY, but if you've ever worked on something that actually
         | needed to scale, or something which lived for a long time,
         | you'll know that the only real principle that you have to care
         | about is YAGNI and that you should never, ever build
         | abstractions until you actually need them.
         | 
         | Part of the reason onion still exists is because academia is
         | still teaching what they did almost 30 years ago, because a lot
         | of engineers were taught 30 years ago and because a lot of code
         | bases are simply old. The primary issue with onion layering is
         | that it just doesn't scale. Both in terms of actual compute but
         | also in terms of maintenance. That being said, a lot of the
         | ideas and principles in onion layering are excellent and as I
         | mentioned still in use even in more modern architectures.
         | You'll likely even see parts of onion layering in things like
         | micro-services, and I guess you could even argue that some
         | micro-service architectures are a modern form of onion
         | layering.
         | 
         | The more competent a system is designed, however, is often
         | shown in how few abstractions are present. Not because
         | abstractions are an inherent evil, but because any complexity
         | you add is something you'll have to pay for later. Not when you
         | create it, not a year later, but in five years when 20
         | different people have touched the same lines of code a hundred
         | times you're very likely going to be up to your neck in
         | technical debt. Which is why you really shouldn't try to be
         | clever until you absolutely need to.
        
           | usrbinbash wrote:
           | > you should never, ever build abstractions until you
           | actually need them.
           | 
           | This should be put on the cover of every programming
           | textbook, in very bold, very red, letters.
           | 
           | Right next to: "Measure before you optimize" and "Code is
           | read 1000x more often than it is written"
        
         | orthoxerox wrote:
         | Yes and no. Onion is similar to IO-less Rust or "Functional
         | core, imperative shell" in that it goes one step further from
         | inversion of control/monadic effects and removes all
         | control/effects from the inner layers.
         | 
         | You get some benefits like being able to write very
         | straightforward business logic, but in return you:
         | 
         | - have to constantly fight the entropy, because every day
         | you'll have to implement another corner case that is 2 points
         | if you violate the layer isolation and 10 points if you
         | reengineer the layers to preserve it
         | 
         | - have to constantly repeat yourself and create helpers,
         | because your API layer objects, your domain layer objects, your
         | DB layer objects all look very similar to each other.
         | 
         | Sometimes a transaction script (in Fowler's terminology) with
         | basic DI scaffolding is easier both to write and maintain,
         | especially when the domain isn't rocket science.
        
       | pyinstallwoes wrote:
       | Indirection?
        
       | Etheryte wrote:
       | > Imagine the most annoying possible client of our microservice.
       | Suppose they want a new field, and they insist that it be named a
       | certain way.
       | 
       | This shows a fundamental misunderstanding of what code is for, at
       | least in a production setting. Code isn't a marble statue that's
       | supposed to be safely guarded in a museum, it's a way to get
       | things done. Sure, for a hobby project you can polish it and
       | perfect it like a diamond, but for business needs, the end goal
       | of code is to do The Thing. That annoying client is why you're
       | getting a high salary, if they didn't have their annoying needs,
       | you wouldn't have a job.
        
         | doctor_eval wrote:
         | There is a lot more to meeting a customer's needs than blindly
         | doing what they say. Sometimes, you need to write code to
         | protect them from their own (sometimes-but-not-always rational)
         | decisions - or to protect other customers and stakeholder from
         | them.
         | 
         | You also need to keep your own team nimble. I've seen plenty of
         | systems that have just thrown code at the product, and they end
         | up with SQL tables with 150 columns - most of which nobody
         | uses, and which can't be removed, and the customer ends up
         | pissed off anyway because you can no longer turn their requests
         | around quickly.
         | 
         | A good, layered design mitigates against this and makes it
         | possible to continue to be productive even in the face of
         | customer demands.
         | 
         | Externalising customisations is a huge benefit to productivity.
        
       | lexicality wrote:
       | this looks like it's describing the typical java horror where you
       | have to make a generic interface to every single dependency to
       | pretend you might be able to swap them out at some point and then
       | you drown in a sea of interfaces and indirection trying to
       | abstract everything into purity
       | 
       | please just write plain code that's easy to follow, easy to debug
       | and easy to delete!
       | 
       | you don't need the survivability onion
        
         | seanhunter wrote:
         | Yes exactly. Anyone who has worked on big java codebases has at
         | least one horror story where there are 10s or 100s of classes
         | each with "use once" interfaces wrapping them and there are
         | bizarre consequences like for example, there is only one
         | possible way to wire the application up but for "flexibility"
         | you have to go through (and occasionally debug) a spring boot
         | nightmare for some reason anyway because somebody read a blog
         | post on dependency injection and thought that was a good idea.
        
       | jpz wrote:
       | This article doesn't define onion layering, I am none the wiser
       | from reading it. There are near identical sentences within it, it
       | gave me the sense I was reading AI generated content, or at least
       | heavily AI post-processed content.
        
         | Quothling wrote:
         | Onion layering is a non-scalable architecture which was
         | immensely popular in the 00-10's due to how it interacts with
         | OOP and it's somewhat crazy principles. It's based on
         | seperating everything by interfaces in a structure which is a
         | little similar to a MVC architecture. So you're going to
         | organize your project(s) into a structure where you centralize
         | everything with a seperation based on what "something" does. So
         | you're going to put your OOP models in a specific place, your
         | services in another and so on. As you can imagine it scaled
         | horribly and today it's made completely obsolete by domain
         | based architecture which is basically Onion Layering but both
         | more reasonably organized and actually scalable.
         | 
         | A lot of CS academics still live in a world where OOP is the
         | greatest thing ever. Where things like wrapping functions in
         | classes are necessary and where things like the onion
         | architecture is still "modern". So naturally a lot of
         | inexperienced developers, or developers who've never had to
         | work on large projects, still hail it as the holy grail.
         | 
         | Note that this was a very opinionated post.
        
           | mech422 wrote:
           | >>Note that this was a very opinionated post.
           | 
           | Bonus points for calling it out as opinion, instead of
           | treating it as the 'one true way' :-)
           | 
           | P.S. I still like OOP well enough, and I love formal
           | interface support like Go/Rust use. (Just IMHO)
        
       | NicoJuicy wrote:
       | Onion, Hexagonal is pretty popular in the dotnet world and
       | microservices.
       | 
       | It's also overcomplicated for it's use-case and the main problem
       | becomes mapping between layers and etc.
       | 
       | Eg. One of the code-smells would be the Web.Abstractions layer.
       | 
       | Just go with Vertical Slice Architecture. It dumbs everything
       | down and if you need another technology as DB for example, you'll
       | always need a migration of sorts, even if you have all the
       | necessary abstractions.
       | 
       | Note: Currently on a solo 4 month migration project going from
       | Cognitive Search to Elastic, yes, we use "Onion".
        
       | dgb23 wrote:
       | I don't know what an onion is in programming terms.
       | 
       | But each of those problems can be solved without one?
       | 
       | ---
       | 
       | 1. "Adding a field to our API"
       | 
       | Parsing: You turn external input into an internal representation.
       | 
       | 2. "Introducing a new downstream dependency"
       | 
       | Essentially the same as above, but presumably bi-directional: You
       | also turn your internal representation into something that is
       | understood by your dependency.
       | 
       | 3. "Introducing a new library dependency"
       | 
       | I don't quite understand this example. Apparently writing a
       | module that uses a third party library is an onion layer?
       | 
       | ---
       | 
       | Can someone explain what I'm missing?
        
         | whalesalad wrote:
         | Separation of concerns, essentially.
         | 
         | https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-a...
        
       | mmastrac wrote:
       | The article is describing, in a less clear way than they could, a
       | clear separation of concerns in code. This is a good practice,
       | especially for complex code.
       | 
       | There is a tradeoff though: adding too many layers of indirection
       | makes changes just as complicated as if you wrote a big ball of
       | spaghetti (oh, just propagate this field across all 10 layers!),
       | and that your team needs to be diligent about making changes to
       | the correct layers.
       | 
       | With a small, high-quality team this absolutely pays dividends in
       | terms of ease of migration and changes, but you need to ensure
       | there is good knowledge of the architecture spread across
       | multiple people.
       | 
       | The ideal place for this is in code with unclear, or rapidly
       | changing business requirements.
       | 
       | Note that this doesn't prevent you from getting bogged down by
       | excessive complexity -- it literally just limits the blast radius
       | of this complexity and makes the inevitable future refactorings
       | smaller in scope.
        
         | chipdart wrote:
         | > The article is describing, in a less clear way than they
         | could, a clear separation of concerns in code.
         | 
         | My take is a bit harsher. The article tries to reinvent the
         | wheel -- poorly -- and fails to do the faintest and most
         | superficial researches on a topic which is already covered in
         | basic courses on software engineering.
         | 
         | Layered architecture is a ubiquitous name in any work on
         | software architecture, such as popular offshoots such as Uncle
         | Bob's Clean Architecture. They describe the benefits of
         | organizing software projects around layers, but go a step
         | beyond and effectively solve the problem by also classify what
         | kind of responsibilities should be assigned to each layer and
         | how the dependency relationships between layers should be
         | managed and enforced. I'm talking about basic principles such
         | as internal/lower-level layers should contain code that changes
         | the least frequently.
         | 
         | There are also some software architectures which abandon the
         | whole concept of layer and instead specify organizing the
         | project in independemt submodules implementing/sharing common
         | interfaces, and have root project handle only integration. This
         | is often dubbed horizontal architecture as in all modules are
         | laid out horizontally, as in they specify no dependency
         | relationships with modules represented above or below, instead
         | of vertically, as in modules are expected to comply with a
         | fixed relationship archetype.
        
         | mcphage wrote:
         | > adding too many layers of indirection makes changes just as
         | complicated as if you wrote a big ball of spaghetti (oh, just
         | propagate this field across all 10 layers!), and that your team
         | needs to be diligent about making changes to the correct
         | layers.
         | 
         | "Layers of abstraction can solve any problem in software
         | design, except for having too many layers of abstraction" :-)
        
       | dekelpilli wrote:
       | Abstracting everything you use has a (high) cost, and doesn't
       | always achieve what you want. This article repeatedly states
       | things "go no further", but realistically, some dependencies will
       | have worse inconveniences than a bad field name. If database you
       | rely on is relational, has token based pagination, and has
       | eventual consistency, chances are that swapping to a consistent
       | timeseries DB with only limit/offset based pagination will impact
       | code in your *.core.* package.
        
       | usrbinbash wrote:
       | > Imagine the most annoying possible client of our microservice.
       | Suppose they want a new field, and they insist that it be named a
       | certain way.
       | 
       | Then I tell the client that I sent him the API specification 2
       | months ago. If he isn't happy with it, he can write a wrapper
       | himself.
       | 
       | Of course, I could also change the API. Whether or not I do that
       | depends on 4 factors in that order:
       | 
       | 0) Do I have the time and resources to do so?
       | 
       | 1) How important is the client?
       | 
       | 2) How much does he pay me to do the change?
       | 
       | 3) Exactly how annoying is the client?
        
       | cangeroo wrote:
       | The article is extremely short and by an unknown author, so there
       | isn't much to discuss.
       | 
       | But I've met many people who hated onion architecture with a
       | passion.
       | 
       | I have a few theories:
       | 
       | - Maybe a lot of programmers have ADHD, are autistic, or suffer
       | from dyslexia, and find planning, naming, designing abstractions
       | as excruciating activities.
       | 
       | - Onion architecture etc. is a long-term strategy that mainly
       | benefits the company/project owner, but not the individual
       | contributor. So it basically has to be forced upon programmers,
       | who will resist it in every way possible, because they have no
       | real incentive to use it.
       | 
       | - It's supposed to make writing software easier. But it really
       | requires an IDE that's designed for abstractions, such as
       | IntelliJ, and also requires a different way of working with the
       | code. It's also verbose. So it's really a different paradigm, and
       | it won't work if you use a plain text editor. You'll drown in
       | code and a vast number of files.
       | 
       | - Onion architecture is not OOP, but often mixed in with
       | enterprise OOP, and therefore bad associations that come with
       | enterprise OOP.
       | 
       | Any other thoughts on why people resist it so much?
       | 
       | And what changes in how we work with code, would make onion
       | architecture more practical?
        
         | victorNicollet wrote:
         | > Onion architecture etc. is a long-term strategy that mainly
         | benefits the company/project owner, but not the individual
         | contributor. So it basically has to be forced upon programmers,
         | who will resist it in every way possible, because they have no
         | real incentive to use it.
         | 
         | Although I was aware of it for a while now, I had never seen
         | this misalignment (between what I called ease-of-writing and
         | ease-of-maintenance) stated so clearly in so few words. Thank
         | you !
        
         | djeastm wrote:
         | >- Maybe a lot of programmers have ADHD, are autistic, or
         | suffer from dyslexia, and find planning, naming, designing
         | abstractions as excruciating activities.
         | 
         | I don't know about the medical conditions, per se, but I think
         | this does bring up a point that is often overlooked when
         | discussing best practices: our brains are different and
         | organize things in different ways.
         | 
         | What works and makes sense to one group of people might not
         | work or make sense to another group of people. I find that more
         | literal-minded people are frustrated by what they see as
         | unnecessary abstraction and are fine with duplicated code
         | whereas people who think in abstractions have no problem seeing
         | the bigger picture and are proponents of abstractions when the
         | abstractions make sense to them.
         | 
         | I have coworkers who will look at a codebase with a
         | layered/onion architecture and immediately understand and reuse
         | all of the abstractions without issue and others who will
         | immediately want to simplify it and change it all into concrete
         | implementations. I find myself to be fairly evenly split so I
         | see it from both sides.
         | 
         | I think it's often more about the nature of the latest person
         | who looks at the codebase than the codebase itself. Eye of the
         | beholder and all that.
        
           | cangeroo wrote:
           | Have you found anything that makes everyone happy and
           | productive?
           | 
           | I sometimes wonder if we'll replace traditional design
           | patterns, especially OOP, with new patterns, that are neither
           | OOP or FP, but perhaps a different paradigm (e.g. how Prolog
           | is wildly different from C++).
        
             | djeastm wrote:
             | >Have you found anything that makes everyone happy and
             | productive?
             | 
             | Not really. It's kind of a constant push-pull and a lot of
             | compromises. In the end it's just about getting things done
             | and dealing with the friction in stride when it comes.
             | 
             | I've often wondered what it would be like working at a
             | workplace where everyone programs with the same mental
             | models, but have never worked at such a place myself and
             | I'm not even sure it's possible.
        
           | mech422 wrote:
           | >>does bring up a point that is often overlooked when
           | discussing best practices: our brains are different and
           | organize things in different ways.
           | 
           | I really notice this when I first look at a code base...Its
           | not that the code is 'bad' but more of a 'what were they
           | thinking' when the code was laid out. Eventually you get used
           | to it, but its a bit of a shock when you first encounter it
           | as the organization, data structures, etc are so 'alien' to
           | how you would organize it yourself...
        
       | joshstrange wrote:
       | I think layers are important (All internal data should be mapped
       | onto DTOs to returned to a client IMHO) but it's also important
       | to not have more than 2 layers to get to the core, ideally 1.
       | 
       | I've worked places before where they have you call through a
       | function that calls a function that calls a function (replace
       | "function" with "api endpoint" in some cases) because "Well we
       | know function X works and so just call it", then fast-forward a
       | year or two "Well we know function Y works, which calls X, so
       | it's safest if you call Y, don't call X" and so on. This leads to
       | a complete mess and tons of inefficiencies. Often "X" over/under-
       | fetches what you want so you either have to throw away some of
       | the result or add to it. I've even seen cases of Z->Y->X where X
       | over-fetches, Y only uses a subset, and then Z has to re-fetch
       | the data that Y threw away. All because someone decided an
       | arbitrary layer is the new golden standard that everything should
       | be built on.
        
       ___________________________________________________________________
       (page generated 2024-07-10 23:02 UTC)