Random thought: using interactors/actions classes isn't OOP. I guess it's OK if it's considered an outer layer of the application. OOP is about messages and objects: state and functions/methods combined form an "object" that has a couple of traits/behavior in the system.
You use a DTO instead, a simpler form of the data. "Internal DTOs" isn't necessarily good: https://philcalcado.com/2008/08/01/internal_data_transfer_objects.html
The whole idea of placing logic in invokable wrappers (interactors, use cases, actions) is missing the point of OOP: their interactions (messages). Most actions/interactors I see are glorified procedures. This is not necessarily a bad thing.
One could argue that procedural code is easier to understand because it's very explicit. Where is the logic? In this function/wrapper (wouldn't call it an "object"). What does it do? Loops over the items, makes some math to update them in the database and dispatch these events.
That's very procedural. And that's OK, tbh. As long as you are aware this is not OOP. I was reading a book and it had a nice example of a parking lot app. It had a Car class, which was just a "databag" and a "ParkingLot" class with the methods "park(Car car)" and "leave(Car car)"
The ParkingLot had the logic to "park" a car and also to "take the car" out of the parking lot. Very simple and easy to understand. Code was wrapped in classes, so that's OOP, right? Nope. The author rewrote the code in pseudo-C and it was exactly the same.
The code was rewritten in OOP. This time, a new class arrived. We had a Car class, ParkingLot was still there, but there was a missing abstraction: a parking space, or ParkingSlot. So, now the ParkingLot had many ParkingSlots that could be available or not.
We still had `ParkingLot.park(car)` but now it would look for an available ParkingSlot (or throw an exception), then it would forward the call to the car: `car.park(parkingSlot)` which would hold the slot on its own state and pass itself to the slot so it could store a ref too.
The Car object knows how to park itself into a ParkingSlot. No need to treat it as a mere "data structure" and leave the logic outside of it. I think the struggle is when we try to map the OOP in a way that can be "stored" (we can't rely on keeping objects in memory all the time)
That's where we get to the repository pattern (glad we are over that era, right?). However, ORMs are also a great abstraction for that. Sure, you will have to make some compromises in order to "fulfill" the ORM, but don't forget this is a hard task: https://martinfowler.com/bliki/OrmHate.html
ORMs allow us to "hide" storage details inside our domain methods in our objects, most of the time. We describe them in terms of relationships (has many, belongs to, etc). I think @adamwathan nailed it on his talk a few years ago called "Chasing Perfect":
As I said, Procedural Code is easier to understand because you don't have to understand the domain to fully understand what is going on. You can simply look at the code, tweak it, and be done with it (sure, like it's that easy).
OOP requires a bit more "commitment" to the system. My current hypothesis (untested) is that "the Basecamp way" works for them because they are working on the same applications for years.
Even though they "rewrite" it every now and then (although it doesn't seem like it's because they can't handle the legacy. It's more like a different version of the application, even a different product. That's a good reason for a rewrite; we tend to be too scared of rewrites).
(I heard once that Google Docs, for instance, was rewritten multiple times - I think about 9 times at the time I heard this, which was years ago. But that's a different topic. Back to OOP).
OOP shines when you have that kind of commitment to the application/domain. Procedural code starts to lure when we have to switch projects very often (even though you get to work on a project for years, but you still have to switch to other projects).
We start adding layers and layers because when we came back to the project a year from now we want to know exactly what the code is doing, even though we don't even remember the nuances of the domain. Procedural code and infrastructure are easier to reason about in that context.
Well, at least that's my current "hypothesis" (I guess at this point I should call it an "opinion" because I'm not actually going to do the research and write a paper on this; not even sure how one could measure it).
Anyways, OOP is about the interaction of objects (messages). Objects are a combination of data and behavior (methods or functions). But I do think there is a place for interactors/actions in objects, as long as it's behind a domain method ("Facade" from GoF):
This thread was much longer than I was planning to do. And this is not even a joke about estimates.

Anyways, what do you think?
You can follow @tonysmdev.
Tip: mention @twtextapp on a Twitter thread with the keyword “unroll” to get a link to it.

Latest Threads Unrolled: