3 ms·
> Which is all great and wonderful - until things change, and requests can suddenly come in without an ID But this is where type systems shine! You create a R
by mrkeen 3y ago
> Which is all great and wonderful - until things change, and requests can suddenly come in without an ID
But this is where type systems shine!
You create a RequestWithoutId and let the boundary accept it (per your scenario). Hit compile and see what fails, in this case, calling the next method into the system will fail, because it expected a different type. Now you can choose to keep plumbing RequestWithoutId through the system - guided only by the type-checker, or you can write a RequestWithoutId->RequestWithId implementation. If your system functions correctly for-all RequestWithId, and you can make a conversion for-all RequestWithoutIds, then your system will function correctly for-all RequestWithoutIds.
But can you turn a RequestWithoutId into a RequestWithId? Inspect the type of Id. If it's a UUID, it's most likely safe to generate in-place, and you're gold. If it's a String or an Int or an Any, then it's no longer mechanical and you probably need to start thinking.
- taeric 3y agoWhere it starts to get more cumbersome than it is worth is requests that can come in with any combination of N fields. The generic path defined here would have RequestWithA, RequestWithB, RequestWithC, etc. And, arguably, yes, that is a good idea. Realistically, nobody wants to review all of that. And you will get phantom code of RequestWithZ that isn't actually valid anymore, but nobody remembers it and there is nothing about the type that makes that clear. In fact, you assume that it has a type, it must be needed. I suspect you will push for groups of the fields that are relevant for the request. Such that it is RequestWithGroupA and such, but, isn't that what "Request" is supposed to be already? The group of data sent? So now you start down a taxonomy of Requests and you build an impressive machine that can deal with this. Which is even more amusing, as that is called a program. And often we accept some invalid possibilities for highly theoretical reasons that help keep the programs easy to read for the people using them.
- mrkeen 3y ago> And you will get phantom code of RequestWithZ that isn't actually valid anymore It is the opposite. My IDE will show "no usages of RequestWithZ". I will not get any such help if it was encoded as Request.get("z").
- majormajor 3y agoNah, your IDE will show all the usages of RequestWithZ, but won't tell you anything about how the thing that sent RequestWithZ has been gone for two years. You'd still need instrumentation with that.
- taeric 3y agoAs the sibling post said, you probably have a ton of code that deals with what happens when a RequestWithZ appears. The thing that changed is that that isn't a thing that can actually be sent to you. Now, you are right that you can contrive a situation where you can choke off all parts that trim this down. And then you can use the type system as a tool to help delete dead code. When it works, it is a great tool. Just like the example in the original article. Please don't take my point as a complete teardown of the idea. Just don't oversell it, either.
- tharkun__ 3y agoExactly! And good tests would be of the type "should accept RequestWithZ under circumstance Y" and in many cases you'll be able to say "Oh, circumstance Y hasn't been current for like 2 years now, we can delete this code!"
- hbrn 3y agoThey seem to shine, but it's a mirage. Once you start naming your types "RequestWithoutId" you have committed to using language alien to everyone but engineers (might even be alien to engineers). There are tremendous benefits in using "ubiquitous language" (in DDD terms), and type-obsession means you'll never get there. Business and engineering goals will never converge. Engineers will live in their own little world, procrastinating in their type puzzles and feeling productive doing so.
- jkaptur 3y agoClear naming is important, and types help with that. "RequestWithoutId" is clunky, but it is easy to explain to an (interested!) business-side person that you have "external requests" (from the outside world) that you validate and transform to "internal requests" (passed around your system). It's certainly easier than if you have no names or types at all!
- Terr_ 3y agoThat's not a problem with the approach, that's a problem with the specific naming. An original request without an ID still has some kind of business relationship, even if only because it really did exist at one point in the past. So there's _some_ proper name out there, even if it's something like InternalRequest or InformalRequest or ObsoleteRequest or BeforeTheMergerRequest.
- kuratkull 3y agoThose kinds of systems are a real joy if designed correctly. You can usually pretty safely assume that the object does not mutate (much), and you can very easily find out what you need to do to pass that object forward into a function that requires RequestWithId. Look up the concept of "make illegal states unrepresentable." EDIT: RequestWithoutId is not the best example of this, but the point remains