Beware of “service objects” in Rails

by Jason Swett,

The good and bad thing about Active Record models

In the early stages of a Rails project’s life, the pattern of putting all the model code into objects that inherit from Active Record works out pretty nicely. Each model object gets its own database table, some validations, some associations, and a few custom methods.

Later in the project’s lifecycle, this pattern of putting everything into Active Record objects gets less good. If there’s an Appointment model, for example, everything remotely related to an appointment gets put into the Appointment model, leading to models with tens of methods and hundreds if not thousands of lines of code.

Despite the fact that this style of coding—stuffing huge amounts of code into Active Record models—leads to a mess, many Rails projects are built this way. My guess is that the reason this happens is that developers see the organizational structures Rails provides (models, controllers, views, helpers, etc.) and don’t realize that they’re not limited to ONLY these structures. I myself for a long time didn’t realize that I wasn’t limited to only those structures.

Service objects as an alternative to the “Active Record grab bag” anti-pattern

A tactic I frequently hear recommended as an antidote to the “Active Record grab bag” anti-pattern is to use “service objects”. I put the term “service objects” in quotes because it seems to mean different things to different people.

For my purposes I’ll use the definition that I’ve been able to synthesize from several of the top posts I found when I googled for rails service objects.

The idea is this: instead of putting domain logic in Active Record objects, you put domain logic in service objects which live in a separate place in your Rails application, perhaps in app/services. Some example service class names I found in various service object posts I found online include:

  • TweetCreator
  • RegisterUser
  • CompleteOrder
  • NewRegistrationService

So, typically, a service object is responsible for carrying out some action. A TweetCreator creates a tweet, a RegisterUser object registers a user. This seems to be the most commonly held (or at least commonly written about) conception of a service object. It’s also apparently the idea behind the Interactor gem.

Why service objects are a bad idea

One of the great benefits of object-oriented programming is that we can bestow objects with a mix of behavior and data to give the objects powerful capabilities. Not only this, but we can map objects fairly neatly with concepts in the domain model in which we’re working, making the code more easily understandable by humans.

Service objects throw out the fundamental advantages of object-oriented programming.

Instead of having behavior and data neatly encapsulated in easy-to-understand objects with names like Tweet or User, we have conceptually dubious ideas like TweetCreator and RegisterUser. “Objects” like this aren’t abstractions of concepts in the domain model. They’re chunks of procedural code masquerading as object-oriented code.

A better alternative to service objects: domain objects

Let me take a couple service object examples I’ve found online and rework them into something better.

Tweet example

The first example I’ll use is TweetCreator from this TopTal article, the first result when I google for rails service objects.

class TweetCreator
  def initialize(message)
    @message = message
  end

  def send_tweet
    client = Twitter::REST::Client.new do |config|
      config.consumer_key        = ENV['TWITTER_CONSUMER_KEY']
      config.consumer_secret     = ENV['TWITTER_CONSUMER_SECRET']
      config.access_token        = ENV['TWITTER_ACCESS_TOKEN']
      config.access_token_secret = ENV['TWITTER_ACCESS_SECRET']
    end
    client.update(@message)
  end
end

I think it’s far better just to have a Tweet object.

class Tweet
  def initialize(message)
    @message = message
  end

  def send
    client = Twitter::REST::Client.new do |config|
      config.consumer_key        = ENV['TWITTER_CONSUMER_KEY']
      config.consumer_secret     = ENV['TWITTER_CONSUMER_SECRET']
      config.access_token        = ENV['TWITTER_ACCESS_TOKEN']
      config.access_token_secret = ENV['TWITTER_ACCESS_SECRET']
    end

    client.update(@message)
  end
end

Isn’t it more natural to say Tweet.new('hello').send than to say TweetCreator.new('hi').send_tweet? I think so. Rather than being this weird single-purpose procedure-carrier-outer, Tweet is just a simple representation of a real domain concept. This, to me, is what domain objects are all about.

The differences between the good and bad examples in this case are pretty small, so let me address the next example in the TopTal article which I think is worse.

Currency exchange example

module MoneyManager
  # exchange currency from one amount to another
  class CurrencyExchanger < ApplicationService
    ...
    def call
      ActiveRecord::Base.transaction do
        # transfer the original currency to the exchange's account
        outgoing_tx = CurrencyTransferrer.call(
          from: the_user_account,
          to: the_exchange_account,
          amount: the_amount,
          currency: original_currency
        )

        # get the exchange rate
        rate = ExchangeRateGetter.call(
          from: original_currency,
          to: new_currency
        )

        # transfer the new currency back to the user's account
        incoming_tx = CurrencyTransferrer.call(
          from: the_exchange_account,
          to: the_user_account,
          amount: the_amount * rate,
          currency: new_currency
        )

        # record the exchange happening
        ExchangeRecorder.call(
          outgoing_tx: outgoing_tx,
          incoming_tx: incoming_tx
        )
      end
    end
  end

  # record the transfer of money from one account to another in money_accounts
  class CurrencyTransferrer < ApplicationService
    ...
  end

  # record an exchange event in the money_exchanges table
  class ExchangeRecorder < ApplicationService
    ...
  end

  # get the exchange rate from an API
  class ExchangeRateGetter < ApplicationService
    ...
  end
end

First, the abstractions of MoneyManager, CurrencyExchanger, etc. aren’t really abstractions. I’m automatically skeptical of any object whose name ends in -er.

I’m not going to try to rework this example line for line because there’s too much there, but let’s see if we can start toward something better.

class CurrencyValue
  def initialize(amount_cents, currency_type)
    @amount_cents = amount_cents
    @currency_type = currency_type
  end

  def converted_to(other_currency_type)
    exchange_rate = ExchangeRate.find(@currency_type, other_currency_type)
    CurrencyValue.new(@amount_cents * exchange_rate, other_currency_type)
  end
end

one_dollar = CurrencyValue.new(100, CurrencyType.find('USD'))
puts one_dollar.converted_to(CurrencyType.find('GBP')) # 0.80

Someone could probably legitimately find fault with the details of my currency conversion logic (an area with which I have no experience) but hopefully the conceptual superiority of my approach over the MoneyManager approach is clear. A currency value is clearly a real thing in the real world, and so is a currency type and so is an exchange rate. Things like MoneyManager, CurrencyExchanger and ExchangeRateGetter are clearly just contrived. These latter objects (which again are really just collections or procedural code) would probably fall under the category of what Martin Fowler calls an anemic domain model.

I’ll run through one last example, a RegisterUser service object I found on this post.

User registration example

class RegisterUser
  def initialize(user)
    @user = user
  end
  def execute
    return nil unless @user.save
    send_welcome_email
    notify_slack
    if @user.admin?
      log_new_admin
    else
      log_new_user
    end
    @user
  end
  # private methods
end

There’s no need for this special-purpose RegisterUser object. There’s already a concept in the domain model (if we think hard enough) of a user registration. Just like how the Devise gem has controller names like RegistrationsController, SessionsController, etc., we can consider there to be a concept of a user registration and simply create a new one.

So the above code becomes this:

class UserRegistration
  def initialize(user)
    @user = user
  end

  def create
    return nil unless @user.save
    send_welcome_email
    notify_slack

    if @user.admin?
      log_new_admin
    else
      log_new_user
    end

    @user
  end
end

By doing it this way we’re neither cluttering up the main User object nor creating an awkward RegisterUser object.

Suggestions for further reading

Enough With the Service Objects Already

I really enjoyed and agreed with Avdi Grimm’s Enough With the Service Objects Already. Avdi’s post helped me realize that most service objects are just wrappers for chunks of procedural code. What I wanted to add was that I think it’s usually possible to refactor that procedural code into meaningful objects. For example, in a piece of schedule code I recently wrote, I have a concept of an AvailabilityBlock and a mechanism for detecting conflicts between them. Instead of taking the maybe “obvious” route of creating a AvailabilityBlockConflictDetector, I created an object called an AvailabilityBlockPair which can be used like this: AvailabilityBlockPair.new(a, b).conflict?. To me this is much nicer. Kind of like the UserRegistration example above, the concept of an AvailabiltyBlockPair isn’t something that obviously exists in the domain, but it does exist in the domain if I consider it to be. It’s like drawing an arbitrary border around letters in a word search. Any word you can find on the page is really there if you can circle it.

Anemic Domain Model

Martin Fowler’s Anemic Domain Model post helped me articulate exactly what’s wrong with service objects, which seem to me to be a specific kind of Anemic Domain Model. My favorite passage from the article is: “The fundamental horror of this anti-pattern [Anemic Domain Model] is that it’s so contrary to the basic idea of object-oriented design; which is to combine data and process together. The anemic domain model is really just a procedural style design, exactly the kind of thing that object bigots like me (and Eric) have been fighting since our early days in Smalltalk. What’s worse, many people think that anemic objects are real objects, and thus completely miss the point of what object-oriented design is all about.”

Martin Fowler on Service Objects via the Ruby Rogues Parley mailing list

This GitHub Gist contains what’s supposedly an excerpt from an email Martin Fowler wrote. This email snippet helped clue me into the apparent fact that what most people call service objects are really just an implementation of the Command pattern. It seems to me that the Interactor gem is also an implementation of the Command pattern. I could see the Command pattern making sense in certain scenarios but I think a) when the Command pattern is used it should be called a Command and not a service object, and b) it’s not a good go-to pattern for all behavior in an application. I’ve seen engineering teams try to switch over big parts of their application to Interactors, thinking it’s a great default style of coding. I’m highly skeptical that this is the case. I want to write object-oriented code in Ruby, not procedural code in Interactor-script.

Don’t Create Objects That End With -ER

If an object ends in “-er” (e.g. Manager, Processor), chances are it’s not a valid abstraction. There’s probably a much more fitting domain object in there (or aggregate of domain objects) if you think hard enough.

The Devise gem code

I think the Devise gem does a pretty good job of finding non-obvious domain concepts and turning them into sensible domain objects. I’ve found it profitable to study the code in that gem.

Domain-Driven Design

I’m slowly trudging through this book at home as I write this post. I find the book a little boring and hard to get through but I’ve found the effort to be worth it.

Summary/takeaway

I find the last sentence of Martin Fowler’s Anemic Domain Model article to be a great summary of what I’m trying to convey: “In general, the more behavior you find in the services, the more likely you are to be robbing yourself of the benefits of a domain model. If all your logic is in services, you’ve robbed yourself blind.”

Don’t use service objects. Use domain objects instead.

  •  
  •  
  •  
  •  

29 thoughts on “Beware of “service objects” in Rails

  1. Dan Milliron

    Don’t dismiss the Interactor pattern/gem. It neatly solves a very common implementation task: creating a single-responsibility class with a sturdy convention for passing in arguments, handling failure, returning results, chaining interactors, and rolling back a chain when one link fails.

    You are correct that the notion of “service objects” has become a hodge-podge of miscellaneous design patterns. Using the interactor pattern for its appropriate subset of domain logic coding is a good idea, leaving the remaining domain logic for other patterns.

    By the way, you and I had a Guinness together at this summer’s RubyHack conference in SLC. We talked about Michigan, if you remember.

    Reply
    1. Jason Swett Post author

      Hey Dan! Good to run into you again, virtually this time.

      The chaining features you describe sound potentially compelling, although I’ve never yet found myself with a chain-shaped hole in my life that I wish something would fill. So I guess if the use case calls for that, then the Interactor gem might be helpful.

      Reply
  2. Guillermo Siliceo Trueba

    It all seems so philosophical i would be convinced of this argument if it was not a discussion of what feels right-er or wrong-er.
    I would love to see examples of the service pattern failing and the command pattern winning instead.
    Otherwise i just go away from this post with the idea of not naming -er objects so instead of AvailabilityBlockConflictDetector with a method detect, to a AvailabilityBlockPair with the method conflict? but the underlying implementation basically the same.
    I just don’t see the concrete problem, just: is better if we name it this other way, because… reasons!

    Reply
  3. Mike Foley

    Thanks, I enjoyed the article. The example code clearly demonstrates that domain objects can lead to more intuitive abstractions.

    Reply
  4. inopinatus

    This is a candidate for Rails Article of the Year. Bravo. It’s everything I think about so-called service objects. Thank you for publishing.

    Sadly, I am absolutely certain that the mediocrity police will not understand (will not _want_ to understand) the nuances that fly off at many angles from this philosophy.

    I subscribe wholeheartedly to the notion that Ruby is an OO language, and Rails is an OO framework, and that going against that grain is setting yourself up for inflexibility and technical debt.

    My favourite exposition of this is Kevin Berridge’s talk, “OOP: You’re Doing It Completely Wrong” (https://vimeo.com/91672848) and it’s tagline, “The Behaviour Emerges from the Composition of the Objects”.

    This is the antithesis of any procedural approach, interactors included. All procedural code makes testing harder, leaves code closed for extension, forces us to depend on concretion not abstraction and so on. Amongst other things, it’s the enemy of SOLID.

    My top guideline is that application code should never be invoking a generic method named #call or something like it, at least not unless the object it’s defined on is representing a telephone, a jury, or a midwife. Quintessential class names, and purposeful method names, are the only way to deliver code that is easy to read & follow.

    As for the naming of things, those who do not grasp the relevance and importance of naming are doomed to reinvent the AbstractSingletonProxyFactoryBean.

    Reply
  5. Douglas Anderson

    I’ve used the Interactor gem extensively in a large codebase and there are two immediate issues which jump out at me from your suggested approach:

    1. Name clashes. Domain objects like Tweet and UserRegistration are nouns and would likely already be claimed by ActiveRecord. Having a service object with a verb-noun combo sidesteps this.

    2. The Interactor pattern follows a common pattern for calling a service, working with context and returning output, while from your examples every domain object would vary.

    Reply
    1. Jason Swett Post author

      Hi Douglas, my response to these would be:

      1. In practice this hasn’t been a problem for me. The non-Active-Record domain objects I’ve created tend have really specific names like, for example, AppointmentSlot, DateTimeAdjustment, and DisallowedInterval. There’s rarely a clash with the AR objects because for me to create a non-AR object with a name matching an AR object would be redundant anyway.

      2. I don’t see what’s bad about the API for different objects being different, and in fact this way seems much more logical to me than if they were the same, since after all they do different things.

      Reply
  6. Dmitry

    “Interactor” is usually used for encapsulating some business action in your application, it launches the interactions between objects/domain objects which are related to this action and process their result. It is very useful if used for this purpose.

    The problem in the examples above(except `UserRegistration`) that the pattern is used for another purpose:
    ex., from the article, “Also, what if you wanted to use the same functionality in another controller? … Why can’t the Twitter API just come with a single prepared object for me to call?”
    The author should encapsulate twitter message sending logic in an ordinary class to solve this, that’s all.

    With regard to `UserRegistration`,
    “There’s already a concept in the domain model (if we think hard enough) of a user registration.”
    Yep, and this “concept” describes the business action and return its result(success/fail). Usually you have a bunch of such concepts in your application, so it’s useful to have a base interface for them(to run/to process errors);`interactor` gem already has an opinionated solution for this. Though, I also prefer to use a noun instead of verb for describing an interaction and I don’t like their `context` and `orgranizer` things, but this is a different topic.

    BTW, if we take the “interactor” description above for granted, the method that runs interactions might be named `run/call/launch` but definitely not `create`

    Reply
    1. Jason Swett Post author

      Hi Dmitry – sounds like you and I agree on most of these topics. Unfortunately, my experience has not been the same as yours if your experience is that the Interactor gem is mostly used for business *actions*. My experience has been that the developers discover Interactor and then shoehorn EVERYTHING into it. As you can imagine, that doesn’t turn out great.

      Regarding your comment about the `create` method name – I would agree if I were trying to make my version match the service object/Interactor version, but I was deliberately trying to make it different. In my mind, a `UserRegistration` gets *created*, so it makes sense for me to call the method `create`.

      Reply
  7. Sammy

    Well sometimes you have some procedure that just needs to be done. Like:
    1 Save some object
    2 Notify some subsystem
    3 Send out an email

    If you strictly go with domain objects then you will probably have callbacks/hooks that makes the next thing happen. These callbacks will make a lot of things more awkward. (Now you cannot create a “some object” in your test setup without having stub out the notify/email logic)

    Also, what’s stopping you from saying that TweetCreator is part of your domain and it’s also just a domain object?

    Naming things in a good way is important, but names don’t change your architecture. I haven’t read the book, but I’m pretty sure domain objects is about designing the right objects and the messages exchanged between them. Not just changing a
    the name of some class/method and saying that you have improved your design.

    Also, I would consider overwriting send (in your first example) typically to be a bad idea.

    Reply
    1. Austin Schneider

      Jason, I think Sammy hits the nail on the head with his comment. Definitely consider it as you write more about this topic.

      I think the right way to think about OO is “what’s the message I need to send?” and “who should receive that message?”, rather than thinking about objects as data + behavior.

      In your tweet example, clearly the message is “send_tweet”. Who should receive this? Probably a twitter service/client of some kind. The tweet is the argument:

      twitter_client = TwitterClient.new
      tweet = Tweet.new(“hello!”)
      twitter_client.send_tweet(tweet)

      Reply
  8. Josh

    The most difficult thing when using domain objecs inside rails applications is that they are often conflicting with my models in terms of naming, becuse the names of my models are the names of my domains.

    For example in your article, RegisterUser.new(user).execute becomes UserRegistration.new(user).create.

    Only thing that changed here is the naming (RegisterUser becomes UserRegistration, execute becomes create).

    Isn’t user registration part of the user domain here? Instead of passing user instance to an object that is basically a function shouldn’t this be written as user.register.

    So for me it is hard to do DDD inside rails without conflicting with my models, do you have any advice/thoughts on this, or am I getting this whole DDD thing wrong?

    Reply
    1. Jason Swett Post author

      I’ve actually never had this naming problem, so the way you approach it and the way I approach it are probably different.

      The `UserRegistration` example is probably not necessarily a great one to focus on. I only used that example because it came from a blog post I was wanting to refute. I would probably never actually end up with an object called `UserRegistration` in a real Rails project (because I would use Devise and let Devise take care of almost all that stuff for me).

      Here are some better examples of real objects I’ve created in a production project. These examples come from a scheduling module. Class names include `AppointmentFilter`, `AppointmentSlot`, `DateTimeAdjustment`, `DisallowedInterval` and `FirstOccurrence`. None of these classes (which are all POROs) map to database tables. I do have a class called `Appointment` which maps to an `appointments` table. The `Appointment` class itself does relatively little. I’ve tried to push most of its behavior out into smaller, more specific objects.

      Reply
  9. Anton

    Great article, although I’m sure (and we can even see it in comments) that most developers will find that difference subtle and mostly philosophical. Still your approach is correct and is in fact more “Rails way”.

    The thing is all these “service objects” are basically fancy Plain Old Ruby Objects, you don’t even need to name a thing like “NewRegistrationService”. The idea of class that just calls 1 method is silly.

    Instead spamming “CurrencyTransferrer”, “CurrencyExchanger”, etc classes it’s more correct to have one Currency class and corresponding methods.

    Anyway, thanks for the article, totally agree with it!

    Reply
    1. Jason Swett Post author

      Thanks. The difference extends well beyond naming, and I find the difference not philosophical but highly practical. Based on the comments I see, I gather that I have more work to do in explaining what I mean. I plan to write some more soon to help make things clearer.

      Reply
  10. Andrew Stewart

    I have found service objects / the command pattern useful for moving logic out of controllers, not models. The controllers become simple adapters between HTTP-land and the domain, reducing coupling.

    Reply
  11. Jon Leighton

    I’m late to the party but I enjoyed this post, thanks.

    Personally I always find it easier to figure out a good domain model when working on non-Rails code bases.

    I think Rails’ fixation on grouping classes by the type of thing they are (model, controller, …) rather than the area of the domain that they relate to, coupled with its aversion to namespacing, makes “thinking outside the MVC” much harder. So some people say, “aha, we need a new box to put some bits of code in — services!”

    Reply
  12. RajaRaviVarma

    I always obsessed about not creating objects which are not noun. But the more I write code, I am slightly leaning towards encapsulating procedures in classes with the necessary states (Service classes). This, IMHO, leads to small testable and easy to change code.

    For example, I have the text transcription of a conversation. There I need to remove some personally identifiable information, which involves calling a third party API. Now where should I put the logic for that? Definitely not in the `Transcription` class. After removing the sensitive data I have to redact the corresponding audio file, but only if there is a need to redact. Otherwise it is unnecessary to do the computation. And it needs to be queued to the background job system. Where should this logic go? Eventually I had to put them in service classes. Would like to know your suggestions on these kind of use cases.

    Reply
    1. Jason Swett Post author

      This is an interesting use case. It’s not immediately clear to me how the redaction work could be expressed in terms of “noun objects”. Maybe this is one of those cases where a procedural object really is better.

      Just to “think out loud” about it, my first instinct would be to have a method on `Transcription` called something like `remove_personally_identifiable_information`. But it sounds like there’s enough stuff going on with the removal of personally identifiable information that it wouldn’t be very nice to have all that in one method. So maybe we’d want to delegate that stuff to one or more other objects.

      One idea I can think of is to have an `UnredactedTranscription` and `RedactedTranscription` class, or something like that. Or maybe `UnredactedTranscription` and just `Transcription`, with `UnredactedTranscription` perhaps inheriting from `Transcription`. The redaction work would of course happen inside `UnredactedTranscription`.

      I imagine I’d probably have more classes than that too but I probably wouldn’t be able to think of what they might be without actually working with the code.

      Reply
  13. Ivan

    I always try to follow same entity creation concepts/naming approach, but I still want to get back to `UserRegistration` class.
    Consider you need more than just #create, but also manage UserRegistration state, for example #cancel, #archive, etc. Will you put all this new logic into the same class? And if so, isn’t this approach not breaking some of concepts, like SRP or lead to God object? Will you convert it to facade then, and if so, who you will delegate actual work to? Maybe UserRegistrationCanceler|UserRegistrationArchiver?
    I think you got the point 🙂

    Reply
    1. Jason Swett Post author

      Good questions. In this case I would consider “cancel” and “archive” to be actions on the user itself, not the user’s registration. The `UserRegistration` class would only make sense to me if a) it only handled registration-related concerns and b) the registration-related concerns were too non-trivial to be included in the `User` class.

      In my experience, cancelation/archival are both trivial enough that they can be included in the `User` class. In fact, there’s usually not anything special in the `User` class at all, it’s just `user.destroy` in a controller.

      Reply
      1. JP

        I’m also interested in expanding on this further. In my experience once you have something like UserRegistration, so much logic eventually gets lumped into it. It’s somewhat abstract to determine if a new method belongs in the class or not, so you get edge cases creeping in, and logic in UserRegistrations growing.

        The thing I like about the idea of service objects is “one public method per class” so you can easily enforce not having scope creep.

        Are you saying that in naming something like RegisterUser, you take extra care to make the intention of the class very clear, so it would be easy to add a few public methods without issue because of the name of the Domain class here?

        Just trying to understand how to make a hard and fast rule about naming, and what public methods would be allowed in a class like RegisterUser

        Reply
  14. anon

    A user object has no business denoting both the structure and validations that signify a bit of user data and how to retrieve and save/update itself. ActiveRecord as implemented with the ActiveRecord orm in Rails just encourages people to write classes that are data models and repositories at the same time, where your fields can be invisible ,and you can have side effecting call backs that invisibly modify world state outside of your own app.

    I have the pleasure of working with an application that is split into five or six smaller rails applications. That isn’t so bad I hear you say… Trust me it is. You see I am not writing a rails application but I need to either figure out which of the five or six applications has the most correct set of models and copy them directly into my own application and then add a whole bunch of irrelevant dependencies just to make the “models” work or I can copy the models over after I figure out which are the most correct and hack them to take out all of the useless callbacks and other external dependencies that I don’t need.

    Now you might argue that my situation is more of a problem with how these applications were built, and you may be right. However the approach that ActiveRecord took is the root cause of these problems as it required the people who built these things to think outside of how the creators of ActiveRecord/Rails and it’s popular plugins actively encourage these tools to be used in the first place.

    If the data model was separate from the database translation it would be trivial to share models across applications regardless of how complex the business rules were and whether or not they need to access the outside world because those things would be somewhere else.

    Your RegisterUser class example isn’t very good not because it is too trivial but because the first instance should be a module, not a class and some base executer class will probably want to just extend the module. (Possibly at runtime with extend.)

    Some other thing would just execute the execute method if the object it receives as an argument responds to execute. In other words the first example feels alot more like an interface in C#.

    Your second instance of RegisterUser would be better off as a module as well because there is no need for internal state at all. The create function returns the created user but even if we are using an ActiveRecord style model there is no reason to create a class with internal state about a user when we can easily make the potential user a parameter to create.

    A good use of a class in that example would be one in which the actual saving was decoupled to a UserContext instance to borrow phrasing from EntityFramework. A context being the thing that actually handles the data connections and so forth in that ORM. This UserContext instance would be passed into UserRegistration and the created instance would communicate with the given context.

    In short command pattern only seems useless in an Active Record context because the pattern, especially as implemented in ActiveRecord puts too many things together and encourages tightly coupled objects that are a nightmare to try and separate after the
    fact. In a system where things are more correctly separated the command pattern becomes much more useful. See: https://docs.microsoft.com/en-us/ef/core/ for a trivial example of a minimum amount of separation.

    Reply
  15. Ben

    hi Jason

    thx for the post.

    Here are my thoughts on this:

    I am not particularly clear about what a service object is. Nevertheless, I do not see a lot of difference between the ideas you use, and the Toptal author, apart from: (i) the toptal author using the call method (which unfortunately obfuscates meaning and readability), and (ii) having only one method per object. Both yours and his are objects which do particular things: e.g. object.do_stuff. have i understood this correctly?

    (((and a side comment that can safely be skipped: Whenever I see words like: domain object / domain model / business model / application specific logic etc. I can’t help but cringe: these catch words are laden with hidden assumptions which obfuscate meaning and intent – almost like fancy marketing words that nobody can concretely define. I did find an article about it here: https://www.codewithjason.com/difference-domains-domain-models-object-models-domain-objects/ ))

    Reply
  16. Omkar

    Hi Jason,

    Thanks for the article. It’s really well articulated and has detailed examples.

    I had one small doubt, though. I’m working on an API only Rails application. APIs have multiple versions. Sometimes, there are certain things which should only be present in one particular API version.
    For example:
    – I would like to validate params only in the latest version of my API.
    – I want to update the associated models only in a particular version of my API.
    How do we handle these scenarios with Domain objects?
    This year’s RailsConf also had a talk where the host used Service objects to manage versions in his API.
    I really would like to solve this problem in an Object Oriented way rather than a procedural way.
    I am curious about your thoughts on this topic.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *