Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I thought the DDD hype has died down a bit, but I guess not.

Eric Evans (inventor of DDD) has said in recent years that unfortunately once a team is big enough, all the invisible conceptual boundaries between domains blur and disappear. People do NOT have the discipline to do DDD correctly.

But do you know what helps? Having physical boundaries you can't cross so easily. Microservices. Or as I call them... just "services". Eric Evans has said that people using microservices may represent the most successful application of DDD's principles, not because people using them were fans or even aware of DDD, but microservices enforce the patterns DDD Evans was describing.



> But do you know what helps? Having physical boundaries you can't cross so easily. Microservices. Or as I call them... just "services". Eric Evans has said that people using microservices may represent the most successful application of DDD's principles, not because people using them were fans or even aware of DDD, but microservices enforce the patterns DDD Evans was describing.

Sir, your assembly code is so hard to read with all them GOTOs. You should do structured programming, use constructs such as loops to make it easier to follow.

Man, your code is so long written like that, you should break it into functions to make it easier to reason about.

Dude, you have so many functions it's impossible to make heads or tails of the logic, you should break it into classes to be able to abstract away all of this detail.

Bro, you have so many classes I don't even know where to start. You should introduce modules so that it becomes possible to isolate different parts of the design.

Dawg, you have so many modules, you ought to break them into small different runtimes communicating via a rest protocol to isolate different parts of the code.

Choom, you have so many microservices, you ought to break them into different microclusters to separate the different parts of the API.

X'man, you have so many microclusters, you really ought to break them into different miniclouds to spearate the functionality and make it easier to follow.

𐀘𐀐, you have so many miniclouds it's impossible to deal with this code, you really ought to separate it into functional minicloud clusters.


Exactly. We need RECURSIVE DIVISION of compute/state entities.

Our main mistake is that we invent a level of division, and never make it recursive, i.e. a class is badly designed to be an entire module, it can't contain classes (inner Java classes are not what I'm talking about), nor can classes cross machine boundaries easily in many languages and so on.

We need a unit of division that works across machines just as well as it works across files, or across statements in the same file, up from data centers to CPU instructions.

Our languages and platforms are full of the SAME THING but SLIGHTLY DIFFERENT, under DIFFERENT NAMES. You listed many of them. Machine opcode, expression, function, class, module, service etc.


Modules are recursive. Libraries are recursive. Hell, functions and classes are recursive too. I don't think we have any non-recursive code abstraction tool.

Some of the problem is that people need different capabilities from different abstraction layers. The rest of the problem is that there are a lot of incompetent people creating software.


If you start listing what different capabilities you need on each layer, you'll notice it can all be done, as a very lean abstraction, in one layer. I'm working on creating a platform that's doing just that, BTW.

I'll cite Erlang as an example that comes somewhat close to what I mean. An Erlang process is a function, a class, a module, and a remote service, all at once. It can be done. We just never stopped to truly think about it and try.


> We just never stopped to truly think about it and try.

Oh, you are right about this. But a lot of people stopped to think about it. It always seemed to fail due to market failures, like most other innovations in development.


Yes, the network effects of pre-existing platforms are big. But we should keep trying I think. I think such a platform should also be inclusive enough hat it can act as a glue layer for and INSIDE existing platforms, but have richer semantics than say JSON, Protobuff or similar protocols offer.


> I don't think we have any non-recursive code abstraction tool.

Mircoservices, Projects in a monorepo, Infrastructure as code, ...

Sometimes they don't even have abstractions at all.

And there are whole companies being built to make these things even remotely managable.


You might become interested in actor systems.


What's the correct level of division?


It's as I've always said, every function ("unit") should be responsible for a single thing, and it must be developed by an independent company with no knowledge of the other companies involved. Otherwise you just can't do software engineering right.


For that to work, there has to be, somewhere, a clear understanding of how the units all work together to achieve the higher-level goals without violating the higher-level constraints which, in practice, increase in number and scope the higher up the pyramid of abstraction you go. Pace Robert Martin, divide-and-conquer cannot be used to reduce systems-level thinking to doing just one thing, even in an abstract sense.


> For that to work, there has to be, somewhere, a clear understanding of how the units all work together to achieve the higher-level goals without violating the higher-level constraints which, in practice, increase in number and scope the higher up the pyramid of abstraction you go.

Oh, that's an implementation detail. The market will sort it out.


I believe that never works in practice. The thing that binds units together is also code. Only while designing / implementing that code, you encounter the flaws in the interfaces of those units, and conclude that half of the units need to be replaced or their specifications need change.


Sure, that's how things used to be.

But this approach, Microcompanies as it is called (units developed by independent companies), is also partly no-code, in that the specifications and different functional units are glued together using lawyers.


> in that the specifications and different functional units are glued together using lawyers.

Exactly! And it all runs on the invisible hand of the free market.


This only works if you have a blockchain to keep track of it all. ;)


I'm not decided on that. On the one hand, sure, blockchain should be involved! But the single thing per company requirement would mean that the company could do nothing else but a blockchain with nothing on it.

But it does sound enticingly pure, a conglomerate of blockchain companies without further function.


This sounds brilliant!

What's the marketing name for that?


I’ve called it Model-T development[1]. The Model-T brought standardization to building cars where companies only built to a very specific spec. It didn’t matter that some companies would be better than others, or cheaper/expensive. The point was choice, competition, and interchangeable parts. Granted, I don’t actually know about that, but that’s the end result.

[1]: I apparently deleted the post so I’ll have to undelete it and come update this comment.


"Lose coupling"


It's called Microcompanies.

>> Otherwise [without microcompanies] you just can't do software engineering right.

So true


It’s called separating interface from implementation. When using an interface, only make assumptions on what the interface specifies, and not on how it may happen to be implemented. When implementing an interface, only make assumptions on what the interface specifies, and not on how it may happen to be used.


Even this confuses people. Often the name of the interface is something like S3Client. No, the name should be something like PolicyWriter.


this is a good summary of why I pay zero attention to power point decks and blog posts. there is never a right answer and it's just people masturbating in circles. it's not rocket science, half of the time it's not even computer science. design patterns, at a certain point, become full of themselves.


Sometimes it’s just a big-ass software project which should never be touched.


So, in short structure is half the code, is what I always say


I'd argue though that the velocity and amount of functionality between those levels increased significantly.


Eh, I'd argue the amount of functionality has if anything stagnated.

Computers today don't do significantly more than they did 25 years ago. They just do the same things with 1000 times more resources across a global computer network.

Likewise, velocity is startlingly bad if you follow conventional advice.


>But do you know what helps? Having physical boundaries you can't cross so easily.

Rarely have I disagreed with something more strongly :)

If a team lacks the discipline/skill/wherewithal to create good boundaries within a single code base when stakes are at their lowest, they absolutely don't have the skill or discipline to do ahead of time, across N code bases, and with a network in between all of them.

Breaking your system apart too early, and when you know the least about it, and with the rationale that doing so will magically stop the problems that a lack of engineering skill creates, has, at least in the small slice of the world I've encountered, resulted in nightmarish levels of complexity (some of which I'm directly responsible for the reasons stated! (whoops!)).


Why do you need to do it ahead of time? Typically what actually happens is you build a monolith, then note that one team needs to own one chunk, so you split it out into its own service.

For example from my experience, splitting an Auth/User service for security/compliance, or splitting a Payment service for the same reasons.

Or splitting out a core low-level platform layer that per-product teams can build atop, allowing each to iterate faster, while the Platform layer focuses on clean abstractions.

Nothing in DDD or microservices says you need to sit down and design the service boundaries up-front, indeed most of the advice I’ve seen in the community suggests the opposite.


You can disagree with it and blame the developers, but the fact is that DDD fails basically every time, unless physical boundaries stop you from failing. It's like saying a good driver won't have an accident on a sharp corner of an icy road, but yet you have a ton of accidents on that corner on an icy road. Facts are facts. Just as it's a fact you can't hope every organization to be staffed by 100% geniuses who never make a mistake.

Part of the problem is the platform itself.

Most mainstream languages today ALL support shared mutable state. Meaning you can hold a pointer or a handle to something, mutate it, and someone else having the same pointer or handle has it changed under their nose.

When you fetch, say, JSON over the network, the party who sent it to you can't change it under your nose either intentionally or accidentally. It's a snapshot of data you own, and you can make decisions on at your own leisure. Sure, the snapshot may get out of date by the time you send your next API request, but this contract is clear and obvious.

While the entangled meshes of mutable code in say C, C++, Java, Swift, Python, JS, etc. etc. often make the mess unavoidable.

Rust goes to some length to stop this problem, but it's a language that's low-level (by modern standards) and technical, and I don't think anyone uses it for enterprise automation exactly.

Another problem are nominal (vs. structural) type systems. Well I don't have to explain, but when you fetch JSON from an API a nominal type won't stop you from reading that data, you care about the structure.


As far as I know developer. They will mess with all kinds of technology given to them.

You say side effects are a problem but you also say the developer cares about his structure in microservices.

When you can’t handle side effects how will you handle microservices?

You could do the same without microservices by just abstracting in libraries which have a defined API.

Microservices will normally make your application more complex. Developers will mess with them too. They can’t change the JSON but some dev will change the API of some service and read the wrong property…


> unless physical boundaries stop you from failing

It is also possible to just police it. Supervise it. This is partly anathema now with agile etc. But the iPhone is what it is because there were a few dictators at the top saying "no". This is actually more effective than a physical boundary because:

1) People will hack around the physical boundary anyway.

2) It forces a conversation between the supervisor and the developer every time the supervisor raises an issue. At this point the developer may get a chance to learn something about the reasons for higher level structure. But more importantly the supervisor might learn something about what is wrong with the higher level structure.


> It is also possible to just police it. Supervise it. This is partly anathema now with agile etc. But the iPhone is what it is because there were a few dictators at the top saying "no".

But then your project design is now dependent on org hierarchy, and getting the right kind of "people". This isn't guaranteed or easy to do, so I would not consider it a serious situation.


Microservices alone don’t solve the shared mutable state problem, and make it even worse w/ network and eventual consistency.


They solve the mutable shared state problem that I'm specifically talking about. Which is that it's clear which state you own, and which state you don't own.

"Shared" implies shared ownership, this is where the confusion comes from. When you ask an API about a user's profile, it's that API's user profile. But the API response itself is entirely yours. It won't change right under your fingertips. The original profile may change, but you know that the profile is not yours already.


You don't need an HTTP boundary to make your own copy of data.


Do you religiously deep-clone every container you pass to a function? No. So it's one thing to say you can copy your data, another to know your data is guaranteed to be copied.

Swift has value types which use pass-by-value semantics. Containers use copy-on-write. This is a language where you can quickly build value trees that you know are a guaranteed copy, isolated from whoever sent you this tree.

But most languages have no such infrastructure. Java is adding record types, but they're immutable instead of in-place mutable, which is incredibly inconvenient to work with in nested data structures.


> Rust goes to some length to stop this problem, but it's a language that's low-level (by modern standards) and technical, and I don't think anyone uses it for enterprise automation exactly.

Java's new type offerings are immutable (records, with value and primitive types in the works). Hopefully a push in the right direction.


> once a team is big enough, all the invisible conceptual boundaries between domains blur and disappear

Conway's law:

Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization's communication structure.


Precisely. But this is how it should be. The service is a part of the team, but automating the "mundane things" that people can delegate to machines, instead of doing them manually themselves.

This is why, BTW, I'm so dismayed Elon Must fired 85% of Twitter. Now many of their critical services have teams of zero or one developer. Those are basically dead services walking.


> Precisely. But this is how it should be. The service is a part of the team, but automating the "mundane things" that people can delegate to machines, instead of doing them manually themselves.

The organization is often not reflective of what’s actually being achieved. Instead of Catalog, Offer, Order and Shipment you have Alice’s org, Bob’s org and Charlie’s org. If you encode the interfaces between these (impermanent) group boundaries rather than the conceptual boundaries you enter a world of pain.

Charlie might own Order and Shipment because Charlie is the warehouse exec and 20 years ago, when only physical items were sold, all of the order-processing computers were located in a cage in the warehouse. Fast forward to today and the cage is gone, and the order processing system handles digital items and services in addition to physical goods. But good old Charlie still owns the Order and Shipment system because they were baked together 20 years ago.


This is true, but I think things like DDD provide an antidote (or at least, some pressure to organize along different dimensions).

If you are explicitly talking about business domains and bounded contexts, then you can see areas where the Alice/Bob split is not optimal (typically, lots of team coupling across org boundaries). Without those concepts I think it is easier to justify arbitrary political/territorial org structures.

There is no panacea; one can always provide contorted justifications for things. But I think having a framework really helps, and particularly, having one that includes business stakeholders and not just pure technical functions.


"Microservices" are not just an acknowledgement of this law, they are a full blown exploitation of it. They are (supposed to be?) literal implementations of your organisation's boundaries/domains.


Would you have the quote on that? I'm curious.

In my opinion, Microservices is an implementation detail, and the extend to which DDD principles are applied is about people and ownership.


Interested in having the quote as well, and ideally the presentation/keynote/interview that came with it.

From my experience, breaking down a monolith without first identifying and isolating its bounded contexts (as in DDD) is a recipe for disaster. With your domains completely entangled in a single codebase, you won't pull only what you want and you will have to bring a lot of undesired code, behaviours and side effects with it.

The safest recipe, in my opinion, is to break down your monolith in bounded contexts (or ideally, to have focused on it from the start), so you can better reason and understand the advantages and trade-offs of moving each specific domain and responsibilities out of your unique codebase.

I saw often teams being motivated in moving their code out of a legacy codebase because of the huge technical debt, but they were bound to suffer a death of a thousand cuts by just bring the same debt in a new service, and adding network constraints on top.


> Microservices is an implementation detail

It is. But it's a very strong one, where it's harder to blur the boundaries because you're putting your code into a service or another.

And that's the advantage. Not because it's easier to apply correctly. But because it's harder to do it badly.

And if you do it badly, it will show and the product performance will be a mess.


Indeed, Uber re-discovered Bounded Contexts not that long ago:

https://www.uber.com/blog/microservice-architecture/

(Though only after they felt the pain of thousands of disorganized services.)


Did he elaborate on how big he thought the team needed to be before microservices were required for DDD?


No plan survives contact with the enemy. But without a plan, neither will you.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: