3 ms·
(I loved this conversation by the way, so thanks!) Just a couple last major things: > make-the-impossible-unrepresentable ... ECS clearly falls in with the dy
by feoren 3y ago
(I loved this conversation by the way, so thanks!)
Just a couple last major things:
> make-the-impossible-unrepresentable ... ECS clearly falls in with the dynamically typed / NoSQL camps
I see why you think that, but I disagree: you still make the impossible unrepresentable. The difference is that "impossible" depends on your perspective -- on the "domain layer" you're working in. Lower-level domain layers are less strict than higher-level ones. Negative numbers are really useful -- your Number System should allow it; it's up to the higher-level consumers of your Number System to decide whether negative numbers should be possible in their ruleset. So type InventoryReading { date, product, amount } can verify that amount is not negative. It can even define amount to be a PositiveInteger if it wants; that's fine. But what is or is not "impossible" turns out to be extremely dependent on your perspective.
> So if I were actually going with ECS (or ECA), I'd actually go ahead and build a type system on top of it...
You're actually overthinking a bit here. No need for higher-kinded types. Just make structs-of-structs-of-structs. If you have a "Facility System" that works with "Facility Models" like { USPostalAddress address, IEnumerable<NaicsCode> naicsCodes, ContactInfo operatorContact } (all strongly typed), then the Facility System can define a bunch of operations by relying on the USPostalSystem, NaicsSystem, and ContactSystem. It can do its own checks and enforce its own rules, do its own translations, etc., but it does that on top of passing each smaller object to its (generally dependency-injected) subsystems.
Those subsystems can in turn rely on other subsystems. The lower level subsystems are less strict -- maybe the USPostalSystem allows PO Boxes, but your Facility System does not consider that a valid address for its uses. So USPostalAddress could even have a method like IsPOBox() -- although even better might be something like IsPhysicalBuilding() or something, so nobody else has to even know what a "PO Box" is. Or to go further into making invalid states unrepresentable, you could have "PhysicalAddress" as the restrictive type here, and your AddressSystem implementation(s) would know how to translate that into their less-strict type after their own checks (can't be a PO Box).
Of course there's an obvious question if we loop way back to what started this whole thing: "but aren't you categorizing things as a Facility right now?"
Naming things is hard. Really hard. The important thing is how I think about what "Facility" represents -- a (rather complicated!) statement that can be made about an entity. Deep-rooted in the philosophy of this design is the idea that I never consider that list to be the comprehensive list of all things anyone might call a "Facility", nor must everything in that list meet all the requirements that anyone might have on a "Facility". It's really just a collection of statements I want to make about some entities. I'm very aware that this definition of "Facility" is highly opinionated and volatile, and that many other people will have a different definition. In fact, that's largely the point of the architecture. So nowhere still have I defined hard boundaries for a "Facility" category. Nobody familiar with this design should be surprised if they encounter a competing definition of "Facility" -- we can handle both! It's just the names that are hard.
- BoiledCabbage 3y agoThanks for sharing thoughts, it's been a great discussion. I'm gonna call it an end here (otherwise I could go on forever), but if you do ever end up putting up a blog post on it, please do share!