browner12's avatar

Repositories vs Services

I've watched both the 'Validation Services' and 'Repositories Simplified' videos, and the ideas presented in them seem to me like there's some overlap. What is the difference between 'Services' and 'Repositories'? Are they complementary or substitutes?

The only thing that strikes me right away is maybe Repositories are for fetching data while Services are for inserting/updating data.

A little confused by this so I'm curious what everyone else thinks.

thanks.

0 likes
2 replies
psmail's avatar

I am not the best of this, but perhaps even if my response blows at least the ball is rolling in that someone can say how it blows and give you the right answer!

My understanding is that repositories are where you put logic to get at your data. Stuff like eloquent calls and DB:: calls and raw SQL statements. It seems to be ok to leave elementary (ie. $user = User:findOrFail(1)) data calls in your controller, but more complex data calls should go in a repo.

Now, I've also seen Jay Dub put some of this data logic into models (like, in the case above, a User model) - say, for adding roles to users. I imagine this is done because roles are inextricably linked to users. But making fat / bloated model is not great so ... well, less is more, I imagine.

So about services. My understanding is that if ever you plan to use an alternate implementation of a rep you should access it via a service. You can then easily swap between these alternate implementations. For example, I recently coded up a repo and the underlining database was MySQL. Then I needed to swap over to Postgres, so I kept my original MySQL rep logic, added a new Postgres repo and then added a service so I could easily swap to the new repo, whilst being able to easily swap back to the old one.

But I also believe that if you never plan on having these alternate repo implementations, you don't have to implement a service. Just point straight to the repo. So a repo without a service could make sense. But a service without a repo does not.

I think I have also seen services used to implement Facades, but that's a little off the topic.

DamienAdermann's avatar

There seems to be a bit of a blur between the responsibilities of Services and Repositories.

Some questions might be:

  • Where do I validate? (Service, Repository or even Model)
  • Where do I create an instance of my model to save? (Repo, Controller, Service)
  • Where do I put my secondary affects like emailing a user on signup? (Controller, Service, Event Handler?)
  • Where do I put x that does y?

I think its a good idea to take a step back and have a look at what each is trying to accomplish.

Whats a service? A service is an object that does something for you. E.g. An AccountService might signup a user.

Whats a repository? Its a datastore (often persistent). It takes some data and puts it somewhere safe until you want it later.

Ok thats all well and great but I can make a wonderful object that can do all those things and all the other domain logic I need! It creates my user, validates it, saves it to the database and emails the user when I'm done and its still clean and manageable.

Fantastic! simplicity is always best.

But what about later when your class has now grown and you need to add more responsibilities to that class. Now I want my class to tweet me when someone has signed up. The first step will be to move out your database store into a separate class. This way your logic is free from the constraints of how to I persist this wonderful object I've created.

It has three other major advantages as well.

  1. Now when I test I don't have to wait for my slow database connection. I just mock the repository
  2. My logic is free from my persistent store. I can switch out to a file store if I want.
  3. Allows me to follow a hexagonal architecture. By only coding to a repository interface it keeps all external entities outside of my domain logic

Ok so in short this new abstraction I've pulled out of my logic class has one job. Put my data somewhere else until I need it.

Oh dear. I'm explaining two types of classes and I've already used one up and I've still got creating my model, validation, emailing and my new tweeting functionality and I've only got one more abstraction to explain. Thats alright because a service is so generic it does everything. The key is a service should only do 1 main thing.

If I want to sign a user up I'll have a service that signs users up. Lets call it AccountCreator (we don't need to add the suffix 'Service' because what value does that add to the class). But isn't that class still doing everything except the nitty gritty data persistence?. Yes and no. Just bear with me and I'll explain.

AccountCreator will:

  1. create an instance of User from the the input form data
  2. Sanitise the input
  3. Validate the input
  4. Save if valid
  5. send an email
  6. send a tweet

However the AccountCreator won't actually do any of that. It'll just tell other services to do it. I'll go through the list in a bit more detail.

  1. Create a User instance with a UserFactory
  2. Tell a UserInputSanitiser to sanitise my input
  3. Ask a UserAccountValidator if the sanitised data is valid
  4. Give my User to the UserRepository to store it for later
  5. Tell my EventDispatcher to let any objects listening that I've created a new user
  6. The EmailListener hears that a new User has been created and tells the mailer to send an email for it.
  7. The TweetListener hears that a new User has been created and tells the tweeter to send a tweet.

So really what we have in the end are two different types of abstractions Internal Services(all my domain logic)

and

Gateways to External Services (The objects that tell something external what to do. Eg. Repositories, mailers and our tweeter).

I hope this has opened up your mind a little about why we create so many of these "excessive" classes.

9 likes

Please or to participate in this conversation.