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

"Yoann R." <[email protected]> Sun, 24 Nov 2019 17:41:33 +0100
Newsgroups gmane.comp.programming.test-driven-development
Message-ID <CACG2rXWDF3J6mM2feur2ZKh-qcFxQVx-zyhi=OhBs18f5Zyr1A@mail.gmail.com>
--000000000000a3698f05981a52e3
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

First thing first, it looks strange to look for something by giving the
values it should fetch.
On the UI side I would usually have a get that fetches me data to get a
state of my application. And at some time, I could decide to send a reques=
t
to create something but I would do so explicitly.

To continue with your question, to me, it looks like the  findOrCreate  is
doing one thing too much.

If you are experiencing performance problems, it could be alright to do so=
.
But in the long run, it would be best to split it into two actions as you
stated.
If you have high concurrency you might also think about these two actions
in a transaction, but if the race is not a tight one, you might survive no=
t
having any transaction at all.

my 2 cts, have a nice evening,

Yoann



Le dim. 24 nov. 2019 =C3=A0 15:01, Steve Gordon <[email protected]> a =
=C3=A9crit :

> Pragmatically, in the vast majority of applications much less data is
> required for finding an entry than creating it.  Creating an entry with =
so
> much missing data can create data integrity problems.
>
> So, given that findOrCreate() should need all the data that Create() wou=
ld
> need (not just identifying information), I would find it cleaner to just
> allow Create to return the object whether it was created or already exis=
ted
> (and signal that the entry already existed if the client code cares).
>
> On Sun, Nov 24, 2019 at 1:55 AM J. B. Rainsberger <[email protected]>
> wrote:
>
>> 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 mu=
ch
>> as possible the number of steps. We don't want to force the user to loo=
k 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 =3D
>> patientRepository.findOrCreate(patientIdentifyingInformation);
>>
>> The identifying information might have basics like name, date of birth,
>> it doesn't matter. We can guarantee that registeredPatient now represen=
ts
>> an Entity in our system, either because we found someone that matched t=
he
>> identifying information or because we created one.
>>
>> This appears to violate CQS, but it seems like a good thing to have. So=
me
>> 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::createFromIdentifying=
Information)
>>
>> where find() returns Maybe TIdentifyingInformation and T has a named
>> constructor for creating a T from a TIdentifyingInformation. I'm assumi=
ng
>> here that TIdentifyingInformation is enough to provide all the mandator=
y
>> properties of T.
>>
>> With this design, I don't need a single findOrCreate() function any mor=
e,
>> because the pieces find() and orElse(T::create) communicate the idea we=
ll
>> 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 i=
t
>> more a matter of context or preference?
>> --
>> J. B. (Joe) Rainsberger :: https://tdd.training :: http://www.jbrains.c=
a
>> :: http://www.thecodewhisperer.com
>>
>>=20
>
>

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

View/Reply Online (#35756): https://groups.io/g/testdrivendevelopment/mess=
age/35756
Mute This Topic: https://groups.io/mt/61872506/3268755
Group Owner: [email protected]
Unsubscribe: https://groups.io/g/testdrivendevelopment/leave/6156322/74455=
6386/xyzzy  [[email protected]]
-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-


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

<div dir=3D"ltr">First thing first, it looks strange to look for something =
by giving the values it should fetch.<br>On the UI side I would usually hav=
e a get that fetches me data to get a state of my application. And at some =
time, I could decide to send a request to create something but I would do s=
o explicitly.<br><br>To continue with your question, to me, it looks like t=
he=C2=A0

<span style=3D"color:rgb(80,0,80)">findOrCreate</span>=C2=A0 is doing one =
thing too much.<br><br>If you are experiencing performance=C2=A0problems, i=
t could be alright to do so. But in the long run, it would be best to split=
 it into two actions as you stated.=C2=A0<br>If you have high concurrency y=
ou might also think about these two actions in a transaction, but if the ra=
ce is not a tight one, you might survive not having any transaction at all.=
<br><br>my 2 cts, have=C2=A0a nice evening,<div><br></div><div>Yoann<br><br=
><div><br></div></div></div><br><div class=3D"gmail_quote"><div dir=3D"ltr"=
 class=3D"gmail_attr">Le=C2=A0dim. 24 nov. 2019 =C3=A0=C2=A015:01, Steve Go=
rdon &lt;<a href=3D"mailto:[email protected]">[email protected]</a>&g=
t; a =C3=A9crit=C2=A0:<br></div><blockquote class=3D"gmail_quote" style=3D"=
margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-lef=
t:1ex"><div dir=3D"ltr">Pragmatically, in the vast majority=C2=A0of applica=
tions much less data is required for finding an entry than creating it.=C2=
=A0 Creating an entry with so much missing data can create data integrity =
problems.<div><br></div><div>So, given that findOrCreate() should need all =
the data that Create() would need (not just identifying information), I wou=
ld find it cleaner to just allow Create to return the object whether it was=
 created or already existed (and signal that the entry already existed if t=
he client code cares).=C2=A0=C2=A0</div></div><br><div class=3D"gmail_quote=
"><div dir=3D"ltr" class=3D"gmail_attr">On Sun, Nov 24, 2019 at 1:55 AM J. =
B. Rainsberger &lt;<a href=3D"mailto:[email protected]" target=3D"_blank=
">[email protected]</a>&gt; wrote:<br></div><blockquote class=3D"gmail_q=
uote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,2=
04);padding-left:1ex"><div dir=3D"ltr"><div>Hi, folks. An old issue came ba=
ck to the surface this month in consultation with clients and I&#39;d like =
your opinion. It regards the old &quot;find or create&quot; pattern. It see=
ms to violate Command/Query Separation (as I understand it), but it seems h=
andy and harmless, so I&#39;d like to find out more about what you folks th=
ink about it. Benign? Problematic?</div><div><br></div><div>I imagine using=
 this with the Repository pattern. Let&#39;s say we register a patient in a=
 medical environment and so we need a UI that reduces as much as possible t=
he number of steps. We don&#39;t want to force the user to look up a patien=
t just to discover that the hospital has no record of them, so we allow the=
 user to enter some basic identifying information. This information suffice=
s to either find an existing patient or create a new one if our database do=
esn&#39;t know that patient. The result is something like</div><div><br></d=
iv><div>Patient registeredPatient =3D patientRepository.findOrCreate(patien=
tIdentifyingInformation);</div><div><br></div><div>The identifying informat=
ion might have basics like name, date of birth, it doesn&#39;t matter. We c=
an guarantee that registeredPatient now represents an Entity in our system,=
 either because we found someone that matched the identifying information o=
r because we created one.</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 don&#39;t know whether this is an area where CQS &quot;=
doesn&#39;t matter&quot; or an area where CQS is trying to teach them somet=
hing 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. Dra=
wbacks? Alternatives?</div><div><br></div><div>I was also thinking about ho=
w to design this, and it seems to me like a special case of getOrAbsent(), =
so that I could implement the generic findOrCreate() algorithm with somethi=
ng like</div><div><br></div><div>repository.find(identifyingInformation).or=
Else(T::createFromIdentifyingInformation)</div><div><br></div><div>where fi=
nd() returns Maybe TIdentifyingInformation and T has a named constructor fo=
r creating a T from a TIdentifyingInformation. I&#39;m assuming here that T=
IdentifyingInformation is enough to provide all the mandatory properties of=
 T.</div><div><br></div><div>With this design, I don&#39;t need a single fi=
ndOrCreate() function any more, because the pieces find() and orElse(T::cre=
ate) communicate the idea well enough.</div><div><br></div><div>Thoughts? I=
&#39;m happy to see the discussion meander. Is this a totally-solved issue =
and there&#39;s one clear good way to proceed? or is it more a matter of co=
ntext or preference?<br>-- <br><div dir=3D"ltr"><div dir=3D"ltr"><div>J. B.=
 (Joe) Rainsberger :: <a href=3D"https://tdd.training" target=3D"_blank">ht=
tps://tdd.training</a> :: <span><a href=3D"http://www.jbrains.ca" target=3D=
"_blank">http://www.jbrains.ca</a> :: <a href=3D"http://www.thecodewhispere=
r.com" target=3D"_blank">http://www.thecodewhisperer.com</a></span></div><d=
iv><br></div></div></div></div></div>



<p></p><p></p></blockquote></div>



<p></p><p></p></blockquote></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/35756">View/Reply Online (#35756)</a> |


  <a target=3D"_blank" href=3D"mailto:[email protected]?subj=
ect=3DRe:%20Re%3A%20%5Btestdrivendevelopment%5D%20%22Find%20or%20Create%22%=
20functions%3A%20a%20discussion">Reply To Group</a>
  
    | <a target=3D"_blank" href=3D"mailto:[email protected]?subject=3DPri=
vate:%20Re:%20Re%3A%20%5Btestdrivendevelopment%5D%20%22Find%20or%20Create%2=
2%20functions%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>


--000000000000a3698f05981a52e3--