202205121023 Use values as the boundary
Using values as the boundaries of your classes/objects/functions/services or w/e is important in testing because it gives you isolation for free.
This is a fairly functional approach. The idea is that there's not this class that has dependencies on a different thing that it calls inside of it. There is simply a passing of values in and out of the thing that does work on it.
For testing, we want a functional core with many decisions/paths, with none or few dependencies. These are easy to isolate and unit test. Then we want a shell that brings the dependencies together, but has very few decisions/paths. These are then the candidates for integration tests.
Values become messages. If values are messages, you get concurrency for free because you have a message passing system. Values afford shifting boundaries.
E.g. via the actor model
actor Sweeper
User.all.each { ExpiredUsers.push(user) }
die
end
actor ExpiredUsers
user = pop
late = user.active? && user.paid_at < 1.month.ago
BillingProblemNotification.push(user) if late
end
actor BillingProblemNotification
UserMailer.billing_problem(pop)
end