I'm not sure to see the difference in coupling in your example between Clojure and Java. For both languages, the function that manipulates the customer is coupled to the description of the customer. If you change the key in your Clojure map representing customer data or if you change the name of the attribute on your Java class, both functions will not work as expected. For me, that's what coupling means. A change in one part propagates to the other part. Two things are coupled because they are somehow (logically) linked together and effects propagate over this link.
I’ll give it a shot 😅 So at a former job, we had this very specific Bid class that represented a Bid model, stored in a bids table in the db. It had methods for dealing with it from all possible contexts: public users, vendors, and admins. The problem is, the idea of a Bid is actually pretty different between each group and by the time I left, the Bid model was gigantic with methods like .send_emails
then .send_vendor_emails
and .send_user_acknowledge_emails
. If this were Clojure we could have had a common set of attributes that represented all possible fields from the db, then defined a Bid for each context separately by composing those attributes and having groups of functions for operating on it for each context like company.user/send-emails
and company.vendors/send-emails
. Subclassing addresses some of the surface level problems but in reality it’s the same data type, but have different meaning in each context. I hope that makes sense.