architecture

fubar 2020-11-15T19:59:42.129400Z

Anyone using DDD tactical patterns to model their domain? I’m reading some books on it and the examples are OO, so I’m wondering how to apply it to functional dynamically typed languages

Shantanu Kumar 2020-11-16T08:57:11.167100Z

@jon920 Do you mind sharing which books are you referring to here? Beside others, I read https://pragprog.com/titles/swdddf/domain-modeling-made-functional/ – found it excellent for FP oriented design.

fubar 2020-11-16T13:32:56.168900Z

“Patterns, Principles, and Practices of Domain-Driven Design” and “Microsoft .NET - Architecting Applications for the Enterprise (Developer Reference)”

seancorfield 2020-11-15T20:15:05.130100Z

@jon920 Can you elaborate what part(s) you're finding hard to apply in an FP context?

fubar 2020-11-15T20:17:19.132200Z

Entities, where you have the data and behavior together. In OOP they are advocating that you hide most attributes of the object, and only expose a subset of those attributes as public through getters or an interface of public business logic methods

lukas.rychtecky 2020-11-16T08:44:14.166800Z

As I know, you should not mix entities and service objects. Entities should hold just values (plain data structures) and services classes operate on them.

fubar 2020-11-15T20:18:23.133500Z

But in Elixir for example (I am only starting to look at Clojure) I think of a struct where you could poke at any of those attributes. Maybe you just need to wrap those structs into some kind of aggregate level module that selectively exposes an interface to hide implementation details?

seancorfield 2020-11-15T20:19:59.135Z

I'd mostly use (immutable) hash maps for entities (and have the behavior in its own namespace). In OOP, you're forced to use encapsulation but I always ask "who are you protecting your data from?"

1👍
seancorfield 2020-11-15T20:20:41.135800Z

The Clojure Applied book leans heavily to records for entities, but Alex has said that in a new edition he would use records less and maps more.

Ivan 2020-11-16T10:58:56.168100Z

I think there were performance concerns with maps, while records were faster to manipulate ..

fubar 2020-11-15T20:21:16.136200Z

Interesting, I ordered that book yesterday

fubar 2020-11-15T20:23:13.137900Z

“Who are you protecting the data from”: from the rest of your domain model so that they aren’t binding to information they should not be and duplicating logic or implementing it in a different way

fubar 2020-11-15T20:25:39.140600Z

To make it more concrete, perhaps they should not be setting some of the attributes, only reading them. For a FlightBooking in my book example the departure date setter is private, while the getter is public. So you can only construct a FlightBooking through a constructor that enforces invariants

seancorfield 2020-11-15T20:26:13.141600Z

But that doesn't really apply in a language where all attributes are immutable.

2👍
seancorfield 2020-11-15T20:26:31.142400Z

A lot of "patterns" in the OOP world are workarounds for deficiencies in OO itself.

seancorfield 2020-11-15T20:27:08.143600Z

(the "Core JEE Patterns" book is a classic example of a of lot of "patterns" that are very specifically workarounds for failings in JEE itself)

phronmophobic 2020-11-15T20:27:25.144Z

my take on difference between typical OOP code and idiomatic clojure: it's common in OOP to combine data, identity, and validation. These are packaged together in a class. the problem is that a class is inflexible and can't be taken apart. conversely, in clojure, the data, identity, and validation are all defined separately and then composed as needed to build an application

seancorfield 2020-11-15T20:27:51.145100Z

Most of the Gang of Four stuff becomes just "functions" when you move away from OO, for example.

seancorfield 2020-11-15T20:28:36.146400Z

The concepts of DDD apply but some of the low-level mechanics do not -- are just not needed.

fubar 2020-11-15T20:30:13.148600Z

In this book example (from Patterns Principles and Practices of DDD by Scot Millet/Nick Tune, C# book) the FlightBooking constructor will throw an error if the id, departure date, or customer id are null and their setters are made private. How can we enforce those invariants on a hash or struct/record in Elixir/Clojure? Because there is nothing preventing you from saying {id: “a8c9-7kjc-97ax-7jk2”, departure-date: null: customer-id: null} (hash example sorry if I messed up the Clojure syntax)

seancorfield 2020-11-15T20:30:51.149300Z

Clojure Spec is good for describing those sorts of constraints.

fubar 2020-11-15T20:31:56.150500Z

Interesting

phronmophobic 2020-11-15T20:34:06.152800Z

whether or not {id: "a8c9-7kjc-97ax-7jk2", departure-date: null: customer-id: null} is valid data depends on the domain, where the data is coming from, where it's going to, etc. in clojure, you can validate the data as needed rather than needing to satisfy arbitrary constraints just to specify some data. consider loading some data from another system (either from a file or over the network). that data may or may not satisfy "customer id is not null", but may be valid in its context. it might be invalid in another context though. validation and data are specified separately, as they should be (imo).

seancorfield 2020-11-15T20:35:41.154900Z

Yeah, I think OO inherently conflates validation with the data and with other behavior -- and therefore it loses the differentiating contexts and that has to be "constructed" somehow, which I think is where all this complexity of overlaying context on data and behavior comes from...

phronmophobic 2020-11-15T20:36:59.156600Z

you can still use helper functions to make it easy to construct and manipulate data in a way that's valid for a specific use case

fubar 2020-11-15T20:38:46.158600Z

@smith.adriane Yeah the invariants I am talking about are only within a specific bounded context within the domain layer (roughly the yellow part here in the architecture i am trying to follow). You’re right outside of that the same validations/invariants would not necessarily apply

phronmophobic 2020-11-15T20:38:47.158900Z

spec is neat because not only can you create specs to validate data, but given a spec, it can also automatically generate data that conforms to the spec. it's great for development and testing

seancorfield 2020-11-15T20:39:55.159400Z

I talk about some of the ways we use Spec at work in this blog post https://corfield.org/blog/2019/09/13/using-spec/

fubar 2020-11-15T20:40:56.159700Z

I’ll definitely check out spec. I know someone even made a lib for Elixir inspired by it https://github.com/keathley/norm was what I was thinking of, but I also just found this https://github.com/vic/spec

fubar 2020-11-15T20:41:05.159900Z

Cool thanks

fubar 2020-11-15T20:59:11.162200Z

Good writeup thanks again

Joe 2020-11-15T21:38:49.166100Z

Scott Wlaschin's Domain Modelling Made Functional has some ideas about how DDD translates to FP. It's F# based and invariants are taken care of by types, which obviously doesn't translate to Clojure, but the stuff about the basis of modelling in FP being through composite values transformed by compositions of functions I think comes across pretty well. Personally I find it a much more natural way of modelling than the OO equivalent for most domains.

3👍
Shantanu Kumar 2020-11-16T09:05:13.167500Z

The book’s code repo is here https://github.com/swlaschin/DomainModelingMadeFunctional (uses F#)

2020-11-15T23:10:24.166400Z

i really like the approach shown in the book and have used it a few times since then in node/typescript projects. it worked well for us