[testdrivendevelopment] "Find or Create" functions: a discussion

"J. B. Rainsberger" <[email protected]> Sun, 24 Nov 2019 09:55:36 +0100
Newsgroups gmane.comp.programming.test-driven-development
Message-ID <CA+H_oaiXhdNTf9ORCavpteSwLkrb1dZVkLQ05o4cnM7KKw6X-A@mail.gmail.com>
--00000000000029bcfe059813d0e5
Content-Type: text/plain; charset="UTF-8"

Hi, folks. An old issue came back to the surface this month in consultation
with clients and I'd like your opinion. It regards the old "find or create"
pattern. It seems to violate Command/Query Separation (as I understand it),
but it seems handy and harmless, so I'd like to find out more about what
you folks think about it. Benign? Problematic?

I imagine using this with the Repository pattern. Let's say we register a
patient in a medical environment and so we need a UI that reduces as much
as possible the number of steps. We don't want to force the user to look up
a patient just to discover that the hospital has no record of them, so we
allow the user to enter some basic identifying information. This
information suffices to either find an existing patient or create a new one
if our database doesn't know that patient. The result is something like

Patient registeredPatient =
patientRepository.findOrCreate(patientIdentifyingInformation);

The identifying information might have basics like name, date of birth, it
doesn't matter. We can guarantee that registeredPatient now represents an
Entity in our system, either because we found someone that matched the
identifying information or because we created one.

This appears to violate CQS, but it seems like a good thing to have. Some
individuals struggle with this, because they don't know whether this is an
area where CQS "doesn't matter" or an area where CQS is trying to teach
them something and they can't see what they're meant to learn. I haven't
thought about this in depth in years, so I feel the same way right now.
Drawbacks? Alternatives?

I was also thinking about how to design this, and it seems to me like a
special case of getOrAbsent(), so that I could implement the generic
findOrCreate() algorithm with something like

repository.find(identifyingInformation).orElse(T::createFromIdentifyingInformation)

where find() returns Maybe TIdentifyingInformation and T has a named
constructor for creating a T from a TIdentifyingInformation. I'm assuming
here that TIdentifyingInformation is enough to provide all the mandatory
properties of T.

With this design, I don't need a single findOrCreate() function any more,
because the pieces find() and orElse(T::create) communicate the idea well
enough.

Thoughts? I'm happy to see the discussion meander. Is this a totally-solved
issue and there's one clear good way to proceed? or is it more a matter of
context or preference?
-- 
J. B. (Joe) Rainsberger :: https://tdd.training :: http://www.jbrains.ca ::
http://www.thecodewhisperer.com

-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.

View/Reply Online (#35741): https://groups.io/g/testdrivendevelopment/message/35741
Mute This Topic: https://groups.io/mt/61872506/3268755
Group Owner: [email protected]
Unsubscribe: https://groups.io/g/testdrivendevelopment/leave/6156322/744556386/xyzzy  [[email protected]]
-=-=-=-=-=-=-=-=-=-=-=-


--00000000000029bcfe059813d0e5
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><div>Hi, folks. An old issue came back to the surface this=
 month in consultation with clients and I&#39;d like your opinion. It regar=
ds the old &quot;find or create&quot; pattern. It seems to violate Command/=
Query Separation (as I understand it), but it seems handy and harmless, so =
I&#39;d like to find out more about what you folks think about it. Benign? =
Problematic?</div><div><br></div><div>I imagine using this with the Reposit=
ory pattern. Let&#39;s say we register a patient in a medical environment a=
nd so we need a UI that reduces as much as possible the number of steps. We=
 don&#39;t want to force the user to look up a patient just to discover tha=
t the hospital has no record of them, so we allow the user to enter some ba=
sic identifying information. This information suffices to either find an ex=
isting patient or create a new one if our database doesn&#39;t know that pa=
tient. The result is something like</div><div><br></div><div>Patient regist=
eredPatient =3D patientRepository.findOrCreate(patientIdentifyingInformatio=
n);</div><div><br></div><div>The identifying information might have basics =
like name, date of birth, it doesn&#39;t matter. We can guarantee that regi=
steredPatient now represents an Entity in our system, either because we fou=
nd someone that matched the identifying information or because we created o=
ne.</div><div><br></div><div>This appears to violate CQS, but it seems like=
 a good thing to have. Some individuals struggle with this, because they do=
n&#39;t know whether this is an area where CQS &quot;doesn&#39;t matter&quo=
t; or an area where CQS is trying to teach them something and they can&#39;=
t see what they&#39;re meant to learn. I haven&#39;t thought about this in =
depth in years, so I feel the same way right now. Drawbacks? Alternatives?<=
/div><div><br></div><div>I was also thinking about how to design this, and =
it seems to me like a special case of getOrAbsent(), so that I could implem=
ent the generic findOrCreate() algorithm with something like</div><div><br>=
</div><div>repository.find(identifyingInformation).orElse(T::createFromIden=
tifyingInformation)</div><div><br></div><div>where find() returns Maybe TId=
entifyingInformation and T has a named constructor for creating a T from a =
TIdentifyingInformation. I&#39;m assuming here that TIdentifyingInformation=
 is enough to provide all the mandatory properties of T.</div><div><br></di=
v><div>With this design, I don&#39;t need a single findOrCreate() function =
any more, because the pieces find() and orElse(T::create) communicate the i=
dea well enough.</div><div><br></div><div>Thoughts? I&#39;m happy to see th=
e discussion meander. Is this a totally-solved issue and there&#39;s one cl=
ear good way to proceed? or is it more a matter of context or preference?<b=
r>-- <br><div dir=3D"ltr" class=3D"gmail_signature" data-smartmail=3D"gmail=
_signature"><div dir=3D"ltr"><div>J. B. (Joe) Rainsberger :: <a href=3D"htt=
ps://tdd.training" target=3D"_blank">https://tdd.training</a> :: <span><a h=
ref=3D"http://www.jbrains.ca" target=3D"_blank">http://www.jbrains.ca</a> :=
: <a href=3D"http://www.thecodewhisperer.com" target=3D"_blank">http://www.=
thecodewhisperer.com</a></span></div><div><br></div></div></div></div></div=
>

<div width=3D"1" style=3D"color:white;clear:both">_._,_._,_</div>
<hr>
Groups.io Links:<p>

You receive all messages sent to this group.


<p>

<a target=3D"_blank" href=3D"https://groups.io/g/testdrivendevelopment/mes=
sage/35741">View/Reply Online (#35741)</a> |


  <a target=3D"_blank" href=3D"mailto:[email protected]?subj=
ect=3DRe:%20%5Btestdrivendevelopment%5D%20%22Find%20or%20Create%22%20functi=
ons%3A%20a%20discussion">Reply To Group</a>
  
    | <a target=3D"_blank" href=3D"mailto:[email protected]?subject=3DP=
rivate:%20Re:%20%5Btestdrivendevelopment%5D%20%22Find%20or%20Create%22%20fu=
nctions%3A%20a%20discussion">Reply To Sender</a>
  


|


  
    <a target=3D"_blank" href=3D"https://groups.io/mt/61872506/3268755">Mu=
te This Topic</a>
  

| <a href=3D"https://groups.io/g/testdrivendevelopment/post">New Topic</a>=
<br>



<br>

<a href=3D"https://groups.io/g/testdrivendevelopment/editsub/3268755">Your=
 Subscription</a> |
<a href=3D"mailto:[email protected]">Contact Group Own=
er</a> |

<a href=3D"https://groups.io/g/testdrivendevelopment/leave/6156322/7445563=
86/xyzzy">Unsubscribe</a>

 [[email protected]]<br>
<div width=3D"1" style=3D"color:white;clear:both">_._,_._,_</div>


--00000000000029bcfe059813d0e5--