Is anyone here experienced with DCI? I have always...
# linking-together
d
Is anyone here experienced with DCI? I have always found it intriguing but I haven't spend enough time understanding it or building a prototype. I just saw this talk by Trygve Reenskaug and he mentions the book Clean Ruby, has anyone read it? (Trygve mentions Engelbart and Alan Kay in the talk πŸ˜‰)
c
Hmm, the first things that come to mind with similar themes are MVC, the repository pattern , and Model View View-Model.
Given it's emphasis on OO (either OOP or Kay OO), it doesn't seem like something that I would personally gravitate towards in comparison to patterns more compatible with data-oriented programming (often found in Rust) or functional programming.
There is quite a bit of overlap with these ideas and the big picture of the software I've written. Things like microservices feel like they embody Kay OOP and the OO that Reenskaug describes here. I feel that we can see an advantage to OOP as something which enables a stricter separation of responsibilities, which is essentially what many architectures are currently built around. For example, a Third Party data sync integration is one object, while the browser client is one object, and some auth gateway is another object.
d
DCI have similarities with MVC because both were created by Trygve! πŸ˜„ https://folk.universitetetioslo.no/trygver/index.html IMO the most interesting idea in DCI is about making the
compile
path equal to the
runtime
path. I hate having to play computer every time I read code. Every concept that you mentioned above lacks this.
c
Very familiar, have built a few systems in DCI – also familiar with Jim's book, which is a pretty good dive into DCI but relies on some custom libraries. In my mind, DCI should be doable with language convention alone. Dynamic languages (or langs that support dynamic invocation) are best suited, as method chains will be overridden. DCI also prefers true object-based languages, vs class-based (though Trygve's collaborator Jim Coplien has built DCI systems in C++ and Java, too – and has defined a custom DCI language charmingly called
trygve
). The idea of DCI is essentially that a model can have a "role" snapped onto it as needed, which provides behavior for the model. When you need the system to do something (ie, a use case), you have a use case object load data models, snap roles on them, and the roles provide new functions – run the functions, then release everything (data model objects go back to being plain vanilla without particular role behaviors). What this does is deconstruct & relieve the pressure put on traditional objects (at least the 90s-00s notions of objects) to be all things for all use cases. Instead, a model primarily becomes just Data (the D in DCI) that can perform different Interactions in different Contexts. It cleans up a codebase because what you primarily see are use cases (context object definitions), and then the models and roles that fulfill the use cases. It's easy to learn what a system can do, and the code reveals in a straightforward way how behavior is implemented. I haven't used it outside of database-oriented projects (tables are nice forcing functions to define models). It worked perfectly in rails. I'm not sure how I'd use it in a strongly-typed language. I think it has a lot of untapped potential.
❀️ 1
d
Thanks for the explantion @Chris G!
It's easy to learn what a system can do, and the code reveals in a straightforward way how behavior is implemented.
^ This is the part about DCI that I'm interested the most. Do you have en example a bit more involved than the classic
TransferMoney
? I'm looking into introducing DCI to Ruby/Rails codebase πŸ™‚
Another question, do you have thoughts about having DCI in a frontend-end framework, e.g. React, Elm, Vue, or even vanilla js?
c
Sure... In one system, which provided an infrastructure service, a useful model was a NetworkInterface, that represented fields like eth0, mac address, local IP. It also had enough data that we could assign it a role as a SwitchPort, with functions that could communicate directly to switch firmware. One use case was to ChangeSubnet, to move a machine NIC to a different network. The ChangeSubnet object could load the NetworkInterface(s) required for the request, add the SwitchPort role to the NetworkInterface, call a SwitchPort function to essentially change to a new switch port, and then update the NetworkInterface model fields accordingly. So in the end, the machine ended up on a new network, and the state of the row of the NetworkInterface table was properly kept up-to-date. In our top level folder, there's a ChangeSubnet ruby file. You look into it to see how the business is done, and how the SwitchPort role is defined.
πŸ‘πŸΌ 1
d
Do you ever need to share Roles/methods between Contexts? Is duplication the way to go?
c
Almost never – we kept roles as a folder of modules, but we could have just done it the recommended way of keeping the definitions in the context file
πŸ‘ 1
d
a model primarily becomes just Data (the D in DCI) that can perform different Interactions in different Contexts.
The idea sounds similar to what you mentioned @Cole:
I would personally gravitate towards in comparison to patterns more compatible with data-oriented programming (often found in Rust)
Do have any resources about
data-oriented programming
?
c
As we leverage data oriented programming at Storyscript (which I am defining as a strict separation between the data being defined separate from the business logic). Rust is good at this because it has enum types (ADTs / variant types) which make it quite a bit easier to embed business rules and object states into the data directly. We base much of our code around variant types with MVVM (see wikipedia) in the frontend TypeScript and an ECS architecture (see "Characteristics" on wikipedia) in the editing engine in Rust.
@Daniel Garcia it sounds like our solutions are more focused on a different approach and need overall, though, since all this code is currently solely being used on a per user basis (one engine instance per document). Our engine's architecture is designed to scale well as additional complexity is added over time (such as parsing to type checking to suggestions based on type checking to personalized suggestions based on multiple sources of suggestions)
To my knowledge, it feels like Kay object orientation at the isolated service level is unavoidable (at least I haven't seen an API that wasn't basically a Kay object to pass messages back and forth with)