IMO, this general idea is very powerful for most code. I believe algebraic effect systems will popularize it further by making it a natural pattern. While OO should be written in this way, we are currently in this weird period of programming history where we don't talk about practices and design as seriously as we should.
But, I'm glad you mentioned this approach. It is not hard to understand, it is elegant, and it is as simple as can be. Just requires a tiny amount of glue and forethought.
* Architecture / design pattern lasagna, where breaking through abstraction layers felt like Inception. And adding a data field meant going through all 5 layers of code...
* Inheritance for functionality instead of typing.
* Also, breaking Liskov substitution willy nilly then wondering why things are hard to reason about. (Because your code becomes entirely reliant on the types actually present at runtime, since you can't rely on semantic equivalence.)
* Tacking methods into data objects because that's where the data is. See other comments on this post advocating a "register" function on the Student object, or should it be on the Course class... Answer is neither. Encapsulation is about maintaining invariants. It's not about putting all functions related to students in the Student class.
* Zero unit tests because external dependencies weren't isolated and all the logic is hard coded into exact use cases, which eventually end in hard coded external dependencies...
There can be a lot of code repetition. To me, that's not a bad thing. I've seen people to themselves in knots trying to DRY some common code, just to later have to undo it all because a requirement changed for only one of the code paths. And, as I said, I like my procedures to read like use cases.
I'll see if I can think of anything else...
My code is typically some dumb data objects, static functions to apply rules to that data, and a bunch of interfaces for abstracting external dependencies. The code that ties everything together lives at the entry points, and is hopefully written like one is reading a requirement.
To take the course registration example, the "register" entry point might look something like:
Advantages:* Data objects are dumb. No need to even test them.
* Rules are all static functions, so you can call them anywhere, anytime, including for testing. Extremely flexible and allows easy remixing of rules for new requirements.
* All external dependencies live behind an interface, so they can all be mocked away for testing.