a9ef4ecc
extracted
12. Ivan Nemytchenko - The Curse of Service Object - wroc_love.rb 2024.txt29a4bc224c1e| Status | Model | Tokens (in/out) | Duration | Cost | Nodes/edges | Read set (nodes/edges) | Time |
|---|---|---|---|---|---|---|---|
| completed | claude-opus-4-7 |
561,610
/
16,874
129,765 cached ยท 9,875 write
|
574.2s | - | 25 / 49 | 158 / 2 | 2026-04-17 23:20 |
| failed | claude-opus-4-7 |
RubyLLM::BadRequestError: You have reached your specified API usage limits. You will regain access on 2... | 2026-04-17 16:18 | ||||
[Applause]
hello and thank you I was trying to make
this slide uh as dramatic as possible so
and let's start so I I'm Ian I'm uh
originally from Russia I live for 10
years already in
Serbia um these are two startups I was
helping in the last
years um I I'm on rail since 2006 I'm a
small Ruby on Rails agency owner and uh
I used to work for gitlab as a fun fact
and those are two small projects uh not
completely finished but you still might
find them useful so and why I'm here so
I'm here to play the role of this guy so
I know that service is like a widely
accepted pattern in the community yet
uh um yet we're going to uh question
this so the plan the plan is the
following first of all I'm going to
share uh one method which I kind of
invented few years ago uh I call it o in
pictures and we're going to use it later
in the talk and then we will see how
developers what the what developers talk
about service objects in their articles
and
uh uh what are the benefits what they uh
they think uh service objects given them
and then we're going to talk about roots
of the concepts we will analyze what
guys like Martin Fowler uh Eric Evans
and uh Robert Martin said about service
objects and try to get an idea from that
and we'll see
how service objects look in serious big
projects I will show you some examples
from gitlab
repository and in the end I will show
how I do it and how I think about it and
the last part I think the most is the
most
important
so service object this is like I'm going
to be really bold now service object is
a very strange controversial and harmful
abstraction that doesn't make any
sense and which contradicts with main
architectural principes
and and it doesn't give you any
benefits and I would say in long term
because at the first place it might look
that it actually gives you benefits but
we will see that it actually doesn't
so o p pictures if you're
interested there's a whole uh
presentation on it like from my uh talk
in railson uh there's a QR code and it
will be uh shown later if you will be
interested as well so the fun
part this is the method I invented when
I was teaching my students and I was
explaining objectoriented programming to
them and I really like throwing those
little dudes so I decided why wouldn't I
draw like classes as those little
dudes and then uh I was I decided Well
and actually can draw hands and the way
you can interact with a class with an
object actually you just uh Shake one of
their their hands so and then I started
developing this idea so how the rest of
the ontology would look like and I
figured out that those hands they can be
actually like Robo arms with fingers and
through those fingers uh uh those
methods can accept arguments and
obviously number of fingers represents
number of arguments and those arguments
they go into their brains and uh those
are instance variables and later they
can be reused in other
methods and to get a result of a
function or a method you open this hatch
and you get the
result and private methods is a funny
concept because those are still arms but
they are kind of internal ones so no one
can shake them from outside but the guy
still can use
them uh exception looks like this it's
kind of you still get the result but
from the different
place uh and maybe not the nice one
and with this you
can you can start seeing antipatterns
visually so for example like too many
arguments too many
methods uh to Long
methods uh too many instance variables
too much conditional logic in the
method or too much of everything
so and another and we were talking about
objects for now but what about classes
and I was not sure how to express it
visually and then I came up with the
idea of kind of spawning platform that
has a cartridge and on this cartridge we
have an instructions on what object to
object to spawn
and we have this lever we pull it and
the magic happens and we get an
object and the fun part is that actually
these two things they have the same
ontology and essentially this lever is
the same as hand and uh it also can have
fingers apparently and this is also just
a method so that's the story and I used
this method to analyze the talk of
Sunday Mets and uh to represent how the
diff the the complexity of a class was
changed over time and I find it really
useful so you don't have to analyze the
code you just see how much complexity
have been added so we're going to use
this this method as well
today okay what is service
object and let's see what what
developers say I analyzed like I don't
know a dozen of Articles and collected
uh opinions of various developers so it
they are crucial for enhancing like code
efficiency and maintainability they help
you structure uh improve structure
organization and testability they
encapsulate business logic they make
your code more modular easier to
maintain easier to
test uh avoid code blood and uh CL ing
of the code keep your code clean and
organized encapsulate business logic
simplify development process make
testing easier etc etc single they are
single
responsibility and sometimes you also
get a a bit of criticism like service
objects are crappy and better Solutions
exist but unfortunately author doesn't
say what are those
Alternatives uh okay
and yeah this is a short summary of what
the they said what developers
saying and what do they do let's see the
quote from those
articles and first of all we get this
guy what it does it uh it simply creates
a book uh with some parameters pass to
it and we have a lot of boiler plate
code which essentially yeah uh
just which is essential there just to
get the shape of a service object but
the useful code is very short here so
another one validate discount code
so I I'm not sure like maybe it could be
a validation maybe it could be just a
method in a in a model but yeah for some
reason it's also a service object uh
this one here we also have a mutation
and uh message uh we send email uh to
our user so it looks like some kind of a
business operation here uh another one
my favorite one so
uh and this one here another business
operation which consists of mutation we
update our trip with parameters but also
we go to external service to Google Maps
and fetch uh distance and duration of
the trip and update uh a trip with it
with it as
well uh this one is a little bit longer
and there are few more methods below
so it is authentication Service you
don't have to read in detail what it
does we will uh explore it later in
details and the final one is a class
called tweet Creator which basically a
wrapper uh um around Twitter API to
create a
tweet and what do we see what does this
code do we see some kind of validation
we see a calculation we see a creation
of an entity we see mutation business
scenarios of two kinds mutation plus
notification external service call plus
mutation and we see rer for external
service and uh to me it is a little bit
strange why uh this code is doing so
many different things and it is called
the same but let's continue and and if
we try to come up with a definition at
this point uh the definition will be
this they just do some
stuff and I wanted to find some kind of
more deep philosophical uh idea behind
it like is there one and I decided to
analyze uh what our fathers said about
it so first of all what is s Service
about service objects and to understand
that I decided
to uh go through those famous books of
these guys and in uh his book patterns
of Enterprise
architecture uh Martin Fowler is saying
that basically he doesn't mention the
idea of service object and the closest
concept uh I found there is service
layer and they're going to be a visual
representation of what uh I think they
meant in order to kind of
uh have the whole picture like visually
so first of all there's an application
and there's a domain logic and
application logic and service layer
defines application
boundary also it sets available
operation so they are inside the service
layer uh and it would be fair to say
that inside those
operations uh coordination of domain
objects happen and also service layer
controls trans transactions and I think
it's happening in the same place and
also um yeah uh the same operations they
encapsulate the application business
logic and the final bit is that service
layer services in service layer by
Martin Fowler they coordinate uh
responses but the trick here is that
fower suggest that Services can be
called either remotely or locally so if
we can if we
uh if our application is just a regular
monolith and it's not kind of a
microservice architecture I would say we
can ditch it we don't need it
so uh yeah and Erica
in
dddd the closest concept to service
object in his book is domain services
and the first thing he mentions is that
they are stateless so they just take
some arguments they do their stuff and
that's it uh they are named for an
activity rather than an
entity and they represent significant
business domain processes and operations
SL
operations and they are distinct from
Technical Services this is interesting
so it he doesn't say what are those
Technical Services he's just saying that
they are different
so and those business operations they
have they may have side effects and I
treated is that uh they actually should
have side
effects uh it could be that there will
be no side effects if some condition uh
they do not are not met but uh in princi
principle they there there should be
some and Robert Martin in his book clean
code he's saying that service should
have well defined interface honestly I
don't know what what what he meant but I
drew it so
anyway um it can be a single component
or it can be composed of a several
component I drew it as well and those
components separated by architectural
boundaries
and unfortunately the context in which
he mentions this is that yeah again
microservices and service oriented
architecture so those are kind of it's
not really relevant to our like very
lowkey uh monolithic
World unfortunately but he also mentions
the importance of single responsibility
principle and I think it actually can be
applied to us so let's keep it there
okay so this is the message being sent
to us by the fathers of complexity
management and kind of lots of rules
let's let's try to kind of squeeze it
and I kind of squeezed it a bit and then
a bit more and this is and now we have
crafted our service ruler and with this
ruler we're going to be measuring like
uh the services we met uh uh so domain
Services they should be stateless well
actually we will see it uh a few more
times so don't need to read
it all right let's try to
analyze
and yeah this one it is not stateless
there is a Constructor uh there's some
State being stored in the object so it's
named for an activity not entity uh no
we failed for the rest I would say yes
we we have passed it represents some
business operation it coordinates the
main objects it uh controls trans
transaction uh it complies with single
responsibility principle and it should
have side effects and there's no
application logic all right another one
and it this one fails miserably so um
yeah definitely not a domain
service this one again looks like
another business operation we've got
here but but doesn't really comp not
sure if it complies with SRP because
actually it does two things it it does a
mutation it also calls uh some external
service so we need to think um depends
on how we look at
it another one uh again fails miserably
um doesn't coordinate any domain object
uh doesn't represent business operation
uh but it is state
another business operation we've got
here again doesn't comply with SRP
because it does two things it works with
external service and uh does a
mutation and this
one another business operation we've got
here but we've got a plenty of code here
so and apparently it does a lot of
things and
most of the developers that I ask they
think about s such code that actually it
is uh this thing is single
responsibility like the whole thing is
about user authentication therefore
there's a single responsibility of
authenticating
users and nope unfortunately no but this
is not what Martin follower meant he
meant that there should be only single
reason to change a unit of code and
here we have plenty of
reasons all right uh the final one not a
domain Service as well um works with
external Ser uh with ex external system
it complies with SRP it has side effects
but for the rest no it's not uh domain
service according to the
fathers so what do we
see this code which everyone calls uh
service object they just just do
different kind of
stuff um some of them are definitely not
domain Services none of them mat all
requirements yet developers name them
name them the same they put them in the
same bucket and this is a bit
strange did we achieve what we wanted
maintainability avoid Cod blood I'm kind
of not sure like it's not yet clear
single
responsibility uh uh sometimes we've got
it sometimes not like and we see that
the bigger our uh service object gets
like the less singular responsibility we
have more modular H not sure again
encapsulates business logic yes for sure
we like I think the whole thing is for
the sake of an encapsulation but we
could
encapsulate uh logic in methods as well
so not sure what's the benefit here
and easier to test um again with small
pieces of code yes easier like when it
gets bigger I'm not so sure and about
structure and organization I would say
the same so let's continue what is so
object about service
objects and in other words is there a
reason why they are shaped like
objects so
three pillars of objectoriented
programming encapsulation inheritance
polymorphism encapsulation we see a lot
inheritance I would say it's a really
bad idea to start using inheritance here
and polymorphism I couldn't come up with
a with a reason to use it here so not so
many reasons to use o here like concepts
of oop and three more ideas is there
reason like uh is there a state which
needs to be managed uh in this story is
there a life cycle of an object we are
about to
create can we imagine a situation where
we need multiple instances of a class at
the same place and the answer is
no so uh pretty much nothing tells us
that uh so that we should be using o
here and what is service object let's
try to answer this question again it
looks like it's just a technical
trick uh with no huge idea behind it in
the sake of and I was really trying hard
to like uh find uh this this this reason
and the only one I found the two I found
like moving code from controller
and remember the whole thing started
like we are we're having fed models fed
controllers let's solve this problem
with services and now we have what fed
Services perfect and testing it
separately from controller yes I would
say it's a good
reason uh but later we will see that
actually Services they take more and
more like in in many cases they take a
lot of responsibilities from controllers
so you are kind of testing it separately
from from controllers but still you have
stolen most of the functionality from
controllers so it's not a huge win
actually okay let's see how serious guys
are doing it like they must have like uh
invest it in in it better and they
um they should really understood uh
should have been understand something
and the good thing is that yes the code
is uh open source
and we can see in the Dynamics how the
things have changed over time and I will
be showing just I don't know random
service objects I found there and some
of them they are not too big again there
is a state and those are three hairs
those are uh they are representing uh
utter readers
um so only two arms one public one in uh
One internal and the Constructor doesn't
look scary I see it does something with
caching I would say caching is a
application Level logic but okay fine
whatever another one two internal uh uh
methods one public kind of
okay uh this one already is a bit bigger
five uh private methods and what we see
here we we see that one service calls
another service and also it is inherited
from a thing called Base Service and in
the base service there's a small include
which includes this thing so it looks
the
that like this thing is quite complex
and it looks like this thing is like
they have to invent the whole framework
to manage this kind of complexity and
still it looks complex so let's
continue this guy I had to record the
gift just to show you like the the whole
thing so it has uh about 200 lines of
code and uh but what is
interesting is at the
beginning it was also a very small and
Co and Compact and nice uh service
object with only two private methods but
over time it it grew and grew and it
turned into this
monster so what we see that uh with this
idea of service objects the complexity
grows
Inward
and that's a bad
sign what we see we see that uh service
object they proactively stealing
controllers responsibilities they kind
of prepareing responses they work with
sessions etc etc uh we see that um we
see a mix of application logic with
domain logic inside those service
objects and the
code and this is a really important part
the code is doing different types of
work
and um but
I would say those service objects they
are doing different uh types of work but
we still put them in the same bucket and
call them the same and inside we see
lots of low-level code uh also doing
different types of work and if we
represent it visually it will look
something like
this so if those colors represent
different types of work for example red
is interaction with external system
uh green is
mutation uh yellow is I know business
rules so the picture looks like this and
we put this into the same
bucket so and these are different kinds
of work these guys are doing
so and
uh yeah I let that sink for a while
so what we see few more observations for
from what we've
seen uh we've seen once that there's a
cold chain of services and if we called
if if we allow one service to call
another it basically means that this
service that we've called it can also
call another service and this can call
another and another and another and we
even can get a circular dependency and
I've seen it in the wild and that it
doesn't look nice so we see inheritance
we don't like it as well uh we see that
over time complexity grows inward
doesn't look nice we see the service
layer becomes responsible for
everything and effectively we have
killed ideas of layed architecture ideas
of modularity and ideas of single
responsibility
principle uh doesn't look
nice so the definition of the
curse uh there's no reason for services
to be shaped as
objects they doesn't much with what
father said developers do not actually
achieve what they wanted to achieve
and uh practice actually opposes the
idea
layered architecture modularity and
SRP again this
picture
and what if what if instead of uh
putting different code uh
into uh building blocks of the same
shape we would start finding the right
places and the right shapes for
different kinds of code in our
application
and what if we could uh find a shelf
find its own shelf for different types
of work in our application what if there
would be a shelf for business operation
there will be a shelf for business rules
there will be a shelf for complex
mutations a shelf for interaction with
external
systems and like the place to work for
with data preparation validations Etc ET
Etc
and I really like this quote that
throughout uh throughout our history it
has always been standardization of
components that has enabled creations of
Greater
complexity and I think this
standardization is actually achievable
and I will show you how I achieve it so
how do I do it and how do I think about
it let's take this guy I promised we
will
uh work with him a little bit more
so first uh first thing I'm doing when
I'm seeing uh such code is that I'm
trying to see uh what kind of what types
of work this code is doing and uh short
uh not note that I'm completely fine
with long methods as long they are doing
like the same type of work and it is of
the same level of of abstraction
uh uh I think that most of the code we
write on the daily basis those are just
procedures and if we have a long
business operation it's fine uh for it
to look as a loan method so okay those
are kinds of work this code is doing so
we see some controller level logic of uh
working with session and preparing
responses we see some rendering we see
uh mutations we see business
rules um and we see one
call one interaction with external
service
so yeah let's find that own shelf for
every type of for and some shelves we do
have already and the first first first
shelf would
be um a
model and we can extract those business
rules where we ask a user if account
loged or if it's a first login for from
new IP so yeah we can just put it in the
model it looks like there's already a
place for
it and
for lowlevel details of complex
mutation we will put it into its own
mutator layer layer and mutators they
adjust procedures
again and essentially this uh this is a
method to uh avoid callbacks
completely uh it will be just a
procedure of uh
your model creation and in complex cases
creating one
entity uh during creation of one entity
you actually need to create and fill in
some special way a lot a plenty of
associations so and I just I would just
put this in
this
um in in a procedure and this will
guarantee you that uh in your codebase
there will no longer be half baked
entities like we we already started
create creating some object but some
associ associations are not yet created
this method actually uh helps you avoid
this problem so so yeah I would just
create a folder mutators I would create
a module or a class doesn't matter uh
because it is just a container for
functions and I would just put those two
functions there they will just do their
job and I will just call them from a
service
so and the main Services they are just
business operations they do represent
business operations and it means like we
have a business operation we have one
service and it is also just a procedure
but the code there should be of a high
level of obstruction and this is how it
looks like
so remember we have moved uh mutation
details on a mutator
level uh we have moved business rules to
the model so now our code looks actually
um much more High levish so to say uh I
put interaction with the mailer like
this I don't think it it makes sense to
wrap it into something because it it is
obvious what it what's it doing if it
would be a more complex interation it
would go to separate uh bucket as
well so and of course we want to keep
application logic outside of services
for example permissions check and data
preparation I would also do outside of
services and only call this service with
valid
params um I know that nobody actually
does it but yeah you will see that
actually it does it does a lot of sense
makes a lot of
sense um yeah and this is how controller
would like would look like so it like we
find this user and we don't uh execute
service until we sure everything is
fine and we prepare responses here on a
controller level we work with session as
well here so we let controller to take
care about application
logic
and here's what we've got so on the
bottom we have a our
model then we have a mutator then we
have a user service and then we have a
controller and this is what we had in
the begin
beginning and this is what we've got so
instead of uh having single class which
over time will become bigger and bigger
we have found uh places to put this uh
functionality uh to and we have built
much smaller building blocks and over
time we just get more of those simple
building blocks instead of having like a
bigger uh complex one
yeah this is what we've
got and
uh surprisingly this thing is stateless
it is just a function it is named for an
activity not entity so it's called
authenticate it represents business
operation it coordinates the main
objects it controls transactions via
calling mutators it complies with single
responsibility principle because it
represents all the business logic on a
high level of
obstruction uh it has side effects
obviously uh yeah and the code is of
same level of obstruction um what we've
got here is
modularity what we've got here is
layered architecture what we've got here
is single responsibility principle
because every piece of this code has
only one reason to
change uh yeah and yeah this thing is
much more more easier to test I would
still not test it I would still test
only
controller but that's the separate
story
and if I would like this picture I drew
just to
highlight
uh the idea that on those different
layers we have code of a different uh
obstruction and uh
these guys are doing different kinds of
work because a lot of developers they
just do all right I have a bit of too
much code here I just copy it there I
just copy it there I just copy it there
instead I I insist that you should think
where this code belongs find this place
in the rails uh in the in in the tooling
of rails or invent invent your own layer
and put it there there that would work
just fine so we did a little like very
little tweaking to what rails gives us
here and uh we we've returned it to to
ideas of layered
architecture and let's quickly uh
refactor all those uh service objects
from from the beginning of the talk so
this guy it is just a m mutator right so
if the creat logic gets more complex the
M mutator will grow but the service we
we will don't have to
change this guy I would put it into a
model into the model uh it would be just
a method
legitimate and the finder
method this guy yeah this is a service
this is valid business operation it can
be a service it can be put into
service there's no reason for for it to
be stateful so it's just a
function this guy just a method in in
the model
obviously this one uh this is
interesting because we I think that
we've got more code here than
originally but uh what we've got is that
we separated this manager which uh does
interaction with external service and
our service
yes it has more code but the code is
just simple it's just simple procedure
that like line by line you clearly
understand what what's happening and uh
yeah you are not mixing two things in
one um all right and this guy it will be
a just
uh I I I call them managers the like the
building blocks that are interacting
with external systems and and you can
see
that uh I kind of reverted it because
originally in like the author construct
passed message into Constructor and
instantiated uh rest client in the
method call but actually it should be
the opposite
so so that's it actually and do you
still think that service object was a
good idea I would really love to get
argumented uh kind of ideas and if you
still think that I'm wrong I would love
to discuss that and I hope that you will
get enough of things to discuss during
the lunch so if we have any time for
questions I would love to answer them
[Applause]
Bo oh boom roasted that's what I wanted
to
say uh thank you very much for your talk
I love the examples where you simplify
the service subjects but I wanted to ask
about the inverse uh which is what about
over engineering because gitlab is a
quite a
sophisticated application uh and it has
a very complex even login logic because
of security con concerns I guess um and
most people will probably never work on
an app even close to that big so don't
you think there is a risk that they will
just invent layers for the sake of
inventing layers and add classes for
sake of adding classes where a simpler
approach might be just
enough I insist that this approach is
simpler because we haven't changed much
to the uh to what rails gives us we just
like service layer we do already have
the only thing that we have introduced
so far is mutator level and also in
practice you don't have to add like uh
if there would be a simple user Creation
with one line of code I wouldn't drop it
into mutator and drop it into service
and call this all like the the whole
hamburger I would just keep it huh
just it it it was already a bit long so
but it could be yes it could be so it's
not like those examples like if I would
be refactoring them tomorrow the code
would look a little bit different so
but yeah it's it's it's not the real
problem actually so it could go into
controller and with time you extract it
to mutator that's how I do it basically
uh
so one of your arguments against uh
service object is that service objects
are not not objects and they are not
objectoriented programming however in
your approach you go totally in
Direction opposite of
objectoriented it's and my question is
like when would you use object oriented
Paradigm uh as uh as opposed to
procedural programming which is I assume
what you advocate
well I guess well when you develop some
code some when you extend your framework
when you develop in some kind of
libraries you will need uh
objectoriented you might need
objectoriented approach but yes most of
the code which we are writing on the
daily basis is it should be just
procedures it should be as simple as
possible and when we come up with the
concepts like monets etc etc dependency
injection we're not doing
good this kind of it's good like
for internals for libraries for gems but
in the reg regular code it shouldn't be
there thank you for your talk um you
said that you should use different tools
for different approaches and I curious
did you use interactors and if yes for
which cases did you use it interact
rectors they just add even more boiler
plate and even more to the idea
of service objects so I don't recommend
to use it at all if you you can
simply uh convert it into functions and
it will work just
fine like basically you will decrease
amount of code like significantly if you
just put it into functions into regular
functions he uh thank you for the talk
um do you have any large race
application where this pattern has been
applied
um I've seen
some uh and the two projects I've been
working on I've shown I was uh
practicing
this and I would say in reality it
doesn't always look that nice they
always going to be some amount of dirty
Solutions and sometimes it's justifi F
because sometimes it's just faster and
more pragmatic to do it in a kind
of in that kind of way the only thing
you have to take care about is that uh
this piece of code ideally shouldn't be
replicated so it should be isolated then
it's fine so I can actually yes I will
probably share on Twitter like the
repository where you can see this
approach in practice uh I should have
been added it here so um yeah I will
show it I will I will share it great
thank you uh and one more question do
you find this to be an extension to what
to the patterns you wrote in your
painless Race book like the shapes for
example uh the shapes the shape I don't
think it was a good it was a nice name
essentially it's just a form and this is
kind of thing this is the evolution of
what I was what I wrote in my book and I
need to basically rewrite it and uh yeah
but forms is a very like unfortunately I
wasn't able to add it to my talk because
working with forms is very interesting
concept as well I will probably do
another talk about it yeah thank
you all right uh thanks Ivan for curing
this disease ladies and gentlemen Ivan
nenko
[Applause]