Could you please define model relationships here? That would help a lot.
For starter, you don't really need to have 1 to 1 relationship between a controller and a service. Since a comment must belong to a post, you can't really make a comment without going through a post service. That said, adding a comment can belong to a post service. In this case you don't need a comment service unless you want it. You can inject post and comment repositories to the post service in this case.
second..I suppose the services you're using are acting like transcripts. You don't really perform ANY logic in the services. Logic should belong to models. Services are like glue for models. Services simply call methods in the models to perform an operation.
Well, model relationships are common eloquent relationships like
class User extends Eloquent implements UserInterface, RemindableInterface {
public function dogs() {
return $this->hasMany('Dog');
}
public function posts() {
return $this->hasMany('Post');
}
}
The comment may belong to dog, post, album or photo, so I thought the own service would be nice, but maybe I will reconsider.
And my services are not just transcripts, they contain logic, in fact I am trying to write them, so they contain ALL business logic (so there is not business logic in model)
Jan
May I ask why you put all the logics in the application service classes? I really don't get this part. Business logics belong to models as far as my understanding goes whether they're active record or not
I was developing before with codeigniter and was putting business logic to model before, and it felt OK. But now with Laravel and with Eloquent I think it is not the option anymore (at least for me). With this patterns I am using, I like my Models just sitting in the background defining ORM.
After reading some resources about app arcitecture and laravel I decided that Service Layer may be a good place to put my business logic. It seems like the best idea to me. There may be reasoning that it is not a good idea. Well, I decided this way (after a long period of decision making).
I see what you mean, but I think it really depends on what you mean by business logic.
I do understand that some people avoid adding ANY logics in the active record implantation of ORM such as Eloquent, but you're actually making anemic model in that case. What are my models if I put all the logics in the service classes ? Are service classes my models now?.. Some of the logics, as long as they do not violate SRP, should belong to each objects even if they're active record IMO. If I use active record and treat those active record objects as if they were entities or DAOs, why do I even use Eloquent? I would just use DAOs or data mapper implantation of ORM such as Doctrine 2. And..If you put ALL the logics into the service objects, you will have duplicated code..I guarantee that. I highly recommend reading http://martinfowler.com/eaaCatalog/serviceLayer.html and other articles on the service layer.
I read a lot of discussion on this topic :) I am not going to argue, your reasoning is valid. As are valid the reasons to put the logic into service layer. It really depends on what is meant by business logic and whole concept of application.
I think you can not say it is good or bad to have business logic in service or model.
Actually this discussion is interesting and the "accepted" answer supports your opinion.
Maybe someone else have some input on using service layers and business logic in Laravel?
Well...the problem is Laravel didn't invent a service layer. It's been in the software developer world for such a long time. Laravel is just using it just like any other frameworks. It really isn't larval specific pattern.
Are we talking about application service or domain service? I mean..there're just too many services as you see on the link, but none of them say that the service classes should have business logics (unless you're intentionally writing transcripts, which you said you're not). It supposed to be stateless.
I think..I can say it's bad to put business logic in the service layer unless you're writing transcripts. I do understand that..I have to be extra careful when I say it's bad thing to do something, but..well..I have to say it at least in a specific case like this. It truly is bad unless you're intentionally doing it because the entire application will grow with it.
One more thing since we're on this topic. A controller or a repository doesn't necessarily be involved in a service layer. They're optional, not requirements.
Can you elaborate what transcript mean? Maybe I dont understand completely. And another thing, if there were no services (as by default there are not in MVC) where would the business logic would go? I mean, we are using eloquent in models, it is not conventional model like in other frameworks not using ORM in my understanding.
Does business logic belong to model even with Laravel and eloquent?
I'm really sorry. Not sure why I used 'transcript'. It's transaction script actually.
It's basically procedure way of processing an operation. I'm not a good explainer so I will just make links that I found on google.
http://gunnarpeipman.com/2013/10/transaction-script-pattern/
http://dotnet.dzone.com/articles/transaction-script-pattern
If you can read c# code, http://www.codeproject.com/Articles/32745/Fowler-s-Domain-Logic-Examples-From-Patterns-of-En is a good example as well.
It's up to you what to use. If your application is data driven, you probably not going to have much business logics. In that case, I would be adding business logics into eloquent model. Why not? The reason it's not suggested is because people tend to put every single methods into eloquent object, making it a god object. If you're careful on that, it's okay to put business logic into eloquent as long as it doesn't violates other OOP principles (active record already violates SRP, but that's not..scope of this topic). If your application has a lot of business logics, then you might want to use data mapper orm, not the active record orm.
What frameworks are we talking about? As far as I know, pretty much all the PHP frameworks use active record implantation of ORM, except Symfony 2.
Anyway, I'm not saying that a transaction script is a bad thing if you know what you're doing. I'm just saying that not ALL (emphasizing on this) the business logic should be in the service classes.
What business logics do you think that you should not put into eloquent model?
Ok, just to be clear... I have zero experience regarding a patterns using in app development. I created a few apps, but they were small. Now I want to do things right. I read a lot and try to do the best I can, in the terms of manageability of the code as the app will grow.
I am not saying that business logic should not be in models here, I am just saying that I would never even think about business logic being in models according to all tutorials that I found on Laravel. Everyone just define relationships in model and that is it. Everything else is in controllers.
But then again, maybe I understand wrong, what business logic is. I found this definition:
Business logic is the process, formula, algorithm, decision tree, methodology, query, or any other means used to perform a piece of work that is specific to an application.
But according to this definition, I would imagine that some validation can be in the model, or business rules that allow action/properties of the model
But what about another type of business logic? Say you have a user model and a pet model. Business rule is that user can have only two pets (yes it is non real :). This business rule is defined in model. But where should be the code, that decided what to do, and what to display to user, when he tries to register third pet? Should that also be in model? Is it still business logic? Or is it application logic?
That may be a wrong example, the model can simply send to view some flag that he is not eligible to register another pet, ok... My point is. The sequence of actions that follow some another actions. The whole algorithm (after register user, send the mail, create related models, set some basic attributes, decide what to display after registration is complete based on user input), that simply can not be in model...
I'm too still learning what we're talking about, so take my words with a grain of salt.
Regarding validation: There're options. Unless you think you will have multiple validation rules (for the values that can go into the model), many people enforce the rules in the model when they use active record because most active record ORMs usually have this feature built-in.
If a user tries to register the third pet, the data is coming from the user side to the application layer. Once you get the data from the controller, you can call a service. From the service, you can call a method that checks the business logic then decide either to deny/allow the request. Redirecting users or displaying data on the view, as far as I concern, are application logic.
Pseudo code: There're many ways of doing the same thing, but this is pretty simple example.
class User
{
function canAddPet()
{
return count($this->pets) < 2;
}
}
class UserRepository implements UserRepositoryInterface{}
class UserService implements UserServiceInterface
{
private $petRepo;
public function __construct(PetRepositoryInterface $petRepo)
{
$this->petRepo = $petRepo;
}
public function addPet(User $user, array $petValues)
{
if (!$user->canAddPet()) {
thorw new NumberOfPetExceedException("say something");
}
// do validation on $petValues
// throw exception if needed
// *this can be done in the controller with a validator class as well
$petValues['user_id'] = $user->id;
$pet = $this->petRepo->create($petValues);
return $pet;
}
}
class aController
{
private $userService;
public function __construct(UserServiceInterface $userService)
{
$this->userService = $userService;
}
public function postAddPet()
{
try {
$this->userService->addPet(Auth::user(), Input::get('pet'));
// do something
} catch (Exception $e) {
// do someting
// redirect back with a message?
}
}
}
You're right on the last paragraph. I don't have any argument on that. Sending email...redirecting users..they're application logic.
Hmmm... so am I correct if I say, that business logic consists of business rules? Like:
and this is not the business logic (in fact it is applicatoin logic)?
The latter paragraph seems like business logic to me too... I am a little confused right now :)
Btw, the example that you posted makes sense, and I have it pretty much the same... with the difference, that I would check the number of pets in service
Yes, I think the definition sounds right.
I think I would put "How many times can user enter wrong password before blocking" into the application logic because you somehow need to access session or cookie or a storage to count that..I think. For the permission related logic, they can also be in a separate layer. You can use ACL for that as well.
I mean..I can say that not ALL the business logics should go to a service layer, but I can't say which one is a business logic or not. You can only define your own business rule. It's your application.
For the checking # of pets in the service, I think it's not like it's something we should never do, but I would rather do that in the model if that's my business rule. I would even override save method and call that method in the save if that's something I really have to make sure.
Ok thanks moon0326. nice to have someone to consult these things, as it really is not exact science :) and nothing is 100% correct
no problem. I really need to remind myself these things over and over. Thank you for giving me chances.
Hi, michaeljohnnyb
It is been a while since I started with this architecture, and so far it works really well. In the end I ended with a lot of services, but not 1:1 to controller or repository as I was afraid of.
It's just matter of defining the responsibilities of services.
The example of the customer service is good, it can handle adding a pet too, but I would argue about the name for the service. In my opinion creating a large services with many responsibilities is wrong (as I tend to do). There should be a service called CustomerPet service that will handle adding/removing/updating a customer's pet
For other customer related operations there should be CustomerRegistration, CustomerProfile service, etc...
This way it will follow the Single responsibility principle that these large services like Customer service (or Customer manager as someone would call it) will not.
Again this is my opinion, maybe I am off.
Hey guys,
I have several large applications, which are using different design patterns. The most complicated would be service-repository pattern like the one you're speaking of, but with Commanding involved. So communication goes like this: Controller - Service(s) - Command(s) - Repository(ies) - Model(s).
From my experience, development is much slower at the beginning, but code is really easy testable and decoupled. What I would argue is requirement for Services if you're using CommanBus - but if you're not, Services do have their benefits.
I would strongly disagree that business logic has to be in the model. But problem here you're speaking about different things - canAddPets is not business logic; it's a relationship restriction, which belongs to a model declaration class of course. However, checking are there pets available so users can adopt them - is business, and should be separated from DB/ORM manipulation (so it's either Controller, either Service).
By the way, to be honest - we're not using same terms for Service here as well. From the global perspective, Controllers in Laravel are Services already, and Eloquent is Domain Level manipulation. But original question was about Services and Repositories, which split Domain Level even further. A simple example would be saving User's Pet. Who should create this relationship? From definition, this probably goes to a Repository - place, where we should store all common data manipulations. But where whould we have an email to end user with congratulations about new pet? Controller has nothing to do with this - neither Repository or Model does. This is where additional "Service" level becomes handy, especialy combined with Events and Listeners.
Hi YOzaz,
thanks for your input, which is by the way very similar to how I do things now. I settled with Controller - Service(s) - Repository(ies) - Model(s), without Commands as I found them unnecessary layer when services are in place. I could stick with Commands instead of Services while Laravel 5 has very handy Command bus baked in..but.. well... maybe I refactor my code later.
In services there is business logic, and maybe more "logic" than it should be there, but so far I am very happy with this architecture.
Hi TorshSK,
Do you have to provide service providers and facades for your repositories and services. I had done a similar architecture just like yours but I have to write service providers and facades in order to access my services and Facades. This is in laravel 4.2 though, I hope there's a better way to do it in Laravel 5.0. Without having to add a new line to config/app.php every time I add a new model. I hope you understand me.
Hey n0oper,
you certainly need to create service provider for services and repositories if you are using interfaces. But not facades. And you don't need to create new line in app.php config. You just need to use your classes (models, services, repositories) in the classes that need them and inject them. So for example in the Album repository, I have
namespace App\Repositories;
use App\Repositories\Contracts\AlbumRepositoryContract;
use App\Album; /*album model (in L5 they are in app folder, and i let them there) */
and a constructor based injection
public function __construct(Album $model) {
$this->model = $model;
}
Similar approach is with services. You just use your desired repository in a service class and inject it in constructor. In controller you use desired service class and inject it in constructor.
Since I am using interfaces(contracts) and not the actual implementations, I need to create services providers for repositories and services classes.E.g. for AlbumServiceClass
public function register()
{
$this->app->bind(
'App\Services\Contracts\AlbumServiceContract',
'App\Services\AlbumService'
);
... // other bindings of other service classes
This is not laravel 5 specific, I had this architecture in 4.2 too. Differences are, that in L5 you have to namespace everything and therefor use the "use ..." statements. Nice thing is that there is a dedicated folder for Providers, where you can put your RepositoryServiceProvider (repositories) and AppServiceProvider (services) files with all the bindings :)
Oh and all you need to add to config/app.php are these new providers classes (in my case RepositoryServiceProvider and AppServiceProvider)
Hi TorshSK,
Thanks for your reply I have now decided to structure my app just like yours. If you don't mind can you share your app directory hierarchy, i just want to get a clear idea of where things should go. I tried my own but it broke some things.
It is classic L5 folder structure. "Repositories" and "Services" folder is in "App" folder. I dont think I have changed something that is default in L5. If you have any question, i will be in laravel chat.
Hello,
is Controller <-> Service Layer 1:1 or it can be 1:n. Let's say I'm creating Blog system with PostController and method (action) getSinglePost(). In this method I'm calling PostService, CommentService and ImageService (1) ? Or just simply call PostService which then handles PostRepository, CommentRepository and ImageRepository to get post with comments and images (2). So then it is basically Controller <-> Service <-> Repository 1:1:n. Is there better way for doing this or option (1) or (2) will do the trick ?
Guys, thanks a lot for this thread! I'm refactoring my code now (and it's OMG :D ) that I wrote 6 months ago when I was still learning. It's actually not that bad, but it actually doesn't follow SOLID principles. So I'm tearing it apart piece by piece, and I wasn't sure what I should do with one controller handling CRUD for model and all of this relations (10 relations, method with a 100 lines of code, scary!), etc. I believe I've found the solution starting from here. Thanks again! :)
Sign in to participate in this thread!
The Laravel portal for problem solving, knowledge sharing and community building.
The community