In Domain Driven Design, the domain model is the part which should be made 
to change the least, which is why getting is right is important, and more 
design time should go into it (often it receives the least amount of 
thought). The whole concept of DDD is based around this axiom, that 
changing your domain model will require to most compounding change, and 
should thus be minimized as much as possible.

The most common strategy is to scope things in sub-domains. So that each 
thing work with a data representation and model tailored to them, which 
only has what they need, in the shape that works best for them. Then, you 
map back and forth between their representation and your persistent one. So 
if you changed the persistent representation, you would only need to modify 
the mapping layer.

Another common strategy is to separate reads from writes in your domain. 
Design your domain model for writes. And for read, you just create a data 
fetching layer, which projects from the persistent write model into 
whatever read model you care about. If you need performance, you can add 
caching to those projections. The idea is that reads are cheap, in that 
they are trivial to refactor, while writes are hard, because invariants 
must be maintained, and the risk of corruption is real. So if you do that, 
you would less often need to change your domain model, basically only if 
you need to change the write requirement, but never when read requirements 
are changed.

Keep in mind that domain model is not= to data. That is, a domain 
encapsulates the data your business domain uses, the relationships between 
the data, and the behavior(as in the writes only) that the domain can do on 
that data. The best example is a SQL database schema. It contains the data 
of your domain, it describes the relationship between that data, and it 
generally protects against invariants, like does not allow you to delete a 
row without also deleting its foreign keys. Most often, you'll also have a 
layer in code to keep track of more complex invariants, like if you 
transfer money between two people, it should always reduce from one and add 
to the other equally, and never leave the state inconsistent. Given this, 
you would perform writes using your invariant keeping functions. So you 
might have a namespace for that. These functions could for example get the 
map, let you modify the map, and then update the map in the DB, but 
validate that it was changed in an acceptable way before doing so. Or you 
could have the functions to the update itself. Or a hybrid, where it 
returns a subset of the map to you only. And then you would also have 
another namespace to handle read projections, where you would ask for data 
in a specific format, and it would handle mapping it. Except from those 
reads, you are not able to write back.

On Monday, 10 July 2017 13:05:08 UTC-7, Luke Horton wrote:
>
> Per some recommendations on clojurians #clojure channel:
>
> - namespaced keys and addition over destruction
> - generally keep your data as flat as possible
> - finicky nested over "organized" data models are magnets for repeated 
> restructures into a new "better, more organized" structure
>
>
> On Monday, July 10, 2017 at 2:39:25 PM UTC-4, Luke Horton wrote:
>>
>> This is probably not clojure specific, but it’s a problem for which i’ve 
>> never really found a satisfying in other languages. Maybe clojure has a 
>> better opinion on the matter?
>>
>> How does one evolve/change data (entities) in a system over time without 
>> also having to change every piece that touches that data?
>>
>> e.g. if I had an entity “foo”:
>> {:a 'a
>>  :b 'b
>>  :c {:d 'd}}
>>
>> and I wrote a nice `clojure.spec` for that foo entity, and maybe 
>> formalized it into a record, anything that needed the data could read 
>> straight from built in functions:
>>
>> (get/get-in/... keyword)
>>
>> I could move one step more formal and write “accessors” for compound 
>> data, i.e. 
>>
>> (defn get-a-compound [foo] (str (:a foo) (:b foo) (get-in foo [:c :d])
>>
>> Now at some point I have to change my entity because a business 
>> requirement changed, or the domain model wasn’t correct the first time. 
>> Must now everything that touches foo and follows the clojure.spec 
>> "contract" of the data shape change?
>>
>> What’s the idiomatic clojure way to handle this? Is it a solved problem, 
>> or is this just a reality that everyone must suffer? 
>>
>> Do I write "getters" for every little piece and lose the :keyword 
>> function accessibility? This seems like data hiding, and smells bad. 
>> Do I leave the raw data alone, and write compound getters? How do 
>> protocols fit into this?
>>
>> In short: how does one prevent the shape of the data from bleeding into 
>> the system such that you can evolve your data over time?
>>
>> Any opinions, references, or discussion would be much appreciated.
>>
>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to