Value Object and Primitive Obsession code-smell

Fair point, but I’m also not arguing for “valid at all times”, as that’s impossible. However, I think that’s for the most part, pure semantics. I’m a subscriber to the DDD approach where the business domain cannot be ALLOWED to have an invalid state at any time. This is enforced by strong typing and/or validation where required. This massively reduces bugs, and so my point about IDs and value objects are approaches to enforcing that validity of state.

I think arguing against that doesn’t make much sense, but of course you will have invalid state when a use submits a form, for example, and you have to manage that, but that’s a separate concern. It seems to me to be a bit of a strawman, honestly, or at least somewhat disingenuous - because it’s obvious. You can’t enforce valid state from a user, that’s why we have validation in place to begin with.

Your point about the ID part, what you’re suggesting simply wouldn’t be possible. The value object pattern enforces that (for example), the ID must be an integer, or a ULID, or whatever it is that makes sense in your system. Of course, a developer could certainly just create an id object without an actual valid id value, but the question then needs to be asked - why? Why are you doing this? If it’s for testing purposes, okay - makes sense. But are you doing this in code you’re preparing for production? Bigger questions would need to be answereed there, likely one of maliciousness :wink:

2 Likes

The problem is that a “business domain” which is not allowed to have invalid state at any time is not a real thing that exists in the real world. It can only be constructed with extraordinary harm to the UX, which is what the article painstakingly explains with careful examples.

The perspective that you are coming from is very common. You imagine a situation in which the state is allowed to be invalid for a little while, as a treat, but once it reaches the backend it must necessarily be beaten into shape by validators and never allowed to deviate again. It is this mentality which Wittens argues, correctly, is incompatible with reality.

I say this as a user of computers, not as a programmer: I am absolutely sick of software that is built this way. “Enterprise” software, which has somehow escaped the enterprise and now terrorizes the rest of us everywhere we turn. It’s exhausting. We can do better.

The disconnect, fundamentally, is that you imagine this complexity to be “of the frontend” rather than “of the application”. It is you, not him, who views the world through this lens.

The complexity which some backend engineers like to pretend does not exist is the essential complexity of the application itself. It’s not accidental, and it’s not something to be avoided. Your purpose as a computer programmer is to solve for it properly rather than push it away.

The split between backend and frontend is very real, but amounts to little more than a network connection. The concerns of the application exist as a whole, not in separate parts.

The reason you cannot use tools like cascading deletes to solve the described problem is that the application needs to have full control and understanding of what is being deleted. If the user presses “undo” and a delete cascades, you have now lost the information needed to perform the symmetric “redo”.

If this is not immediately obvious to you it is likely because you have never tried to implement undo/redo in a real app, in which case I strongly recommend you give it a go. That’s the only way things will ever get better.

1 Like

The problem is that a “business domain” which is not allowed to have invalid state at any time is not a real thing that exists in the real world.

Having modelled numerous business domains now, and implemented them, this is absolutely false. It seems to me that you’re blurring lines. The business domain is actually something you -can- control and manage, establishing clear boundaries and ensuring that valid state is the only way in which a bounded context can operate safely. You’re blurring the lines with your point about Enterprise software, and I see such arguments made a lot, that because X is done badly, X must be bad. This is a false dichotomy. And to be honest I don’t really know what you mean by “Enterprise software”. It’s a rather loaded term that means different things to different people.

You imagine a situation in which the state is allowed to be invalid for a little while, as a treat, but once it reaches the backend it must necessarily be beaten into shape by validators and never allowed to deviate again.

Not once it reaches the backend, once it reaches a domain boundary. It’s a subtle but technically important piece.

Also, replying to a separate comment you made to someone else: for context, I have implemented undo/redo and guess what - that was in a DDD application and it was technically gorgeous, with enforced valid state at all times. At the time we could not believe how easily event sourcing (in particular) handled this component, literally out of the box.

Coming back to the topic at hand, let’s think about something like an email address. If you want to work with valid email addresses in your domain, how do you enforce that? Just assume that the string that is provided is always a valid email and push any errors further down your stack, maybe minutes or months after the invalid email was created?

I realise you’re not saying “never enforce valid state”, but it also seems a stretch to extrapolate that out to “valid state is never possible”. My experience (of which I have over 25 years, as a software engineer and CTO), has demonstrated otherwise. In fact, I would go so far as to say that the best applications I have built, have been in the last 5-10 years as I’ve learnt about DDD and the power it brings to software development.

1 Like

This simply begs the question. The burden is on the author to provide arguments in support of this point but unfortunately I think the vast majority of their examples are not well chosen since they concern use cases which very clearly pertain to FE contexts. Decoupling logic designed to handle those cases (which very well might not want to enforce validity!) from BE systems is not “pretending it does not exist.”

1 Like

I only replied because you mentioned game development. I have little interest in a discussion on modeling business domains. I’m sure you’re very good at it, certainly better than me.

I appreciate your attempt to argue in good faith here, but you have still (I’m sure unintentionally) misrepresented my point.

It’s not that valid state is never possible, but that ensuring state is valid at all times as it transitions between valid states is not possible. This is a concept which is hard to understand in the abstract, which is why I provided a link to an article with what are, at least to me, clear and helpful examples.

There are exceptions to everything, and there are cases where you want to guarantee “validity” and normalize data. Bureaucratic business processes are the canonical example. Email addresses are probably not a great example (in fact it is very common not to verify them), but passwords would do just as well, and in fact the same author has another excellent article on that topic.

Of course you have every right to your opinions, but I don’t really care if the author has successfully roasted backend devs or not. This article had immediate utility for me: there are certain types of computer program which I struggled to implement before reading it, and now I do not. So I share it when I can, in case the same is true for someone else. It might not be true for you!

Personally I am not interested in drawing a line between frontend and backend contexts; I think systems should be designed as a whole. But maybe this is orthogonal.

I probably should have been clearer earlier, but I never said they are only about validation.

From the official docs:

Changesets allow filtering, type casting, validation, and constraints when manipulating structs,

For the last 5+ years, I’ve been using changesets to enforce a baseline of correctness in what is, frankly, a pretty YOLO typing environment. If I have the proper tools, I would be doing that at the edge only (I tried that, I tried the discipline route, it didn’t work, and I refuse to blame the hard working talented people I work with)
The main reason I never built a separate package is simple: I didn’t want to add more fragmentation or cognitive load to the ecosystem or to teams. People already know Ecto. That’s good enough, for now.

It has served me well. After 9 years working with Ecto and Phoenix and building production systems in Elixir, I’m comfortable saying I understand when Ecto is appropriate and how to use it responsibly.

Personally, I’m pragmatic about this. I don’t feel strongly either way. At this point, I care more about what works well for teams than about ideological purity.

Anyway, I am moving on from the Ecto discussion.

2 Likes

LOL, uh sorry miscommunication on my part (and sorta misdirected squarely at you). Of course “changes” include validations (hence “hand-in-hand”). The params-manipulation thing is just a huge pet peeve of mine and I always assumed that it stems from people thinking changesets is only about validations. Ok, no need to talk about Ecto any further.

The most senior answer I can give here is: *It depends* haha :upside_down_face:

Jokes aside, generally speaking, I do think that primitive obsession is a real problem, especially when modeling rich domains. However, the opposite can certainly be abused as well.

I guess my rule of thumb is usually to think if the abstraction is useful/ provides anything on top of the raw value, for instance:

a) Is the data correlated? (in case of multiple values)
b) Is the shape static or dynamic? (enforceable keys)
b) Do I need to enforce the shape? (structs vs maps, match by type)
c) Does the shape bring any value to the system? (Does it provide information, improve validation, or overall understanding and maintainability of the system?)

It might be just be me, but I found this article quite boring to read and somewhat pedantic, to be honest, but again, maybe I’m not as enlightened as the author on the matter.

1 Like
1 Like