I am solo developer. I've been in the industry a long time - but have no nearby peers to brain storm or review.
I recently watched laracon eu 2014 videos with much interest.
Its funny how much us developers all have the same struggles and questions.
I watched a talk on Validation and how to lay things out better, rather than putting the validation in the model.
My current approach thankfully already follows a very similar process to Kirk Bushells - but at the end of the talk I was left feeling I need one final bit of advice.
I'm really uncertain if I am adding too many layers to my app.
I have:
A Controller OBJ
A Form OBJ
A Repo OBJ
X number of models.
In line with many of the talks I try to keep my controllers thin.
I use the controller to:
instantiate a Form object, passing in the request data.
$request = $this->contactForm->store( $this->request );
The form object then:
Handles validation. IF successful,
Passes the data to the REPO object
return $this->partyRepo->store( $request )
The REPO works with the models to CRUD some data.
Then I chain the result back to the controller where I handle the result.
I run this through a Translator Object that ensures an acceptable array of data is provided.
Handed to the Dispatch Object to decide, what/how format to send the data to user.
Here's the controller:
$request = $this->contactForm->store( $this->request );
$responseData = array();
if ($request->getStatus())
{
$partyTransformer = new ContactTransformer( $request->getData(), false, false );
$responseData = $partyTransformer->transform();
}
//handle request and send appropriate response data
$response = new ResponseService(
$responseData, //send array in
$request->getHTTPstatus(), //http response status code
$request->getMessage(),
$request->getErrors()
);
//return HTTP cached response
$dispatch = $response->dispatch();
return $dispatch;
The BIG Question
As a dev, and especially as a dev working solo, its easy to second-guess yourself.
Have I put an unnecessary layer in, with regards to the Form Object.
From watching the Laracon videos, I can identify with some points that makes me think I have.
Such as having to use a Value Object as I am now passing data through many layers.
But what are my options. Putting the validation in the Repo? or in to the controller - OH HELL NO!
Ross Tuck said in his talk - asking for advice is what you do when you already know the answer, but dont like it!!
I am kind of feeling that way.
As a SOLO programmer with ZERO peers to review my work: You, the internet, obwan kenobi - are my only hope!!
Good question.
The immediate thought process I would think of when going through this question is - is there a class I am already using that can handle the functionality without breaking the Single Responsibility Principle? If there is, then yes - you've over-complicated/over-engineered it. If you haven't, then you're on the right track.
In respect to your form objects - I actually have setup a more generalised solution - services. Services handle particular use-cases, and will talk to validation and persist data to storage via the repositories. The only issue I take with the term "Form", is that it stipulates that that data will only ever come from a form. And this is probably true for 90% of cases - but what about the times when you have to run a cron-job, or you're seeding data.etc?
I think you're on the right track, but there appears to be a lot of redundant code in your example, in that - it looks like code that would show up in many different controller actions. My thought there is what can you do to simplify it? Exceptions are a good way to do this, which I talk about in my Validations presentation at Laracon. That way you can write your code with the assumption that everything has worked fine - and have response handlers deal with those exceptions when they arise, thereby cutting down your controller code substantially.
Hope that helps :)
Thanks Kirk,
That is definitely food for thought.
I'm scratch my head with regards to your idea of using Custom Exceptions and I can see how this would cut a load boiler plate out.
You also mention "services". I am using Services in my app. Admittedly as a self taught OOP dev - its my notion/heavily influenced by the internet of what that entails.
This is my project structure.
In regards to Single responsibility - I am not certain how to achieve this.
My test project is basically creating a Contact book.
Party = Person or Org with relations X number of telephone X number of Emails X number of Addresses
To Save a New contact, I am using a Repository. Injecting via Service Providers the relevant models and doing the logic in the STORE method.
If I lose my Form Object that is currently handling Validation - Do I put it in the Repo?
The light bulb has gone on in respect of handling failed validation - Custom Exception and event Listener.
But the actual code that exists to loop my JSON payload, then test against the relevant models Repo Injected Validator has to sit some where. If I put this in the REPO does this break single responsibility.
I feel this simple scenario is holding me back from being able to fly the project.
I feel I need someone qualified (more qualified than me) to say - YUP thats the way you do it. ! OR No this is a better way.!
Based on what you write above and taking what experience I have from learning Solo - my best guess on moving forward would be:
Im still not sure.
You write:
In respect to your form objects - I actually have setup a more generalized solution - services. Services handle particular use-cases, and will talk to validation and persist data to storage via the repositories.
I would love to hear more on this. Anything specific or maybe an example.
Once again - THANK YOU - for taking the time to help.
Okay - so firstly, I will admit that your follow up questions I'm finding a little hard to read, so I apologise if the below answer does not address your needs.
Secondly, repositories should ONLY be for creating and managing database objects. What do I mean by this? I mean that any call you make to a repository, should only ever be persisting data, deleting data, or retrieving data. If it's doing anything else for a particular model, then it's doing too much and breaking single responsibility principle.
For validation - put the validation call in the service, but have a separate validation class PER USE CASE. This will allow validation to do only that. I'm working on an article that will highlight this a little more clearly, as well as an article of how to use repositories reponsibly. Let's say for example, you have a user registration use case, the code path should look like this:
POST to /users/register/ (or something)
User controller that handles registration should have a dependency on the UserRegistration service.
UserRegistration service class calls validation, something like:
(new UserRegistrationValidation($input)->validate());
If validation fails, it'll throw an exception which you can handle
Next line should be dealing with the users repository:
$user = $this->usersRepository->registerWith($input);
Make a call to the repository, create the user. You then have a user object you can do whatever you want with.
return $user;
This should be your last line in your service's register method.
Now, back in your controller, you simply handle the $user however you want:
$this->session->logIn($user); // or something of that nature
return Redirect::to('some action');
This obviously isn't the only way to deal with it, there are many different approaches. The problem with using lots of services, or commands if you'er going the DDD route, is how to handle all those layers. What used to be a 3-layer approach (MVC), is now fast becoming 5 or 6 layers. You don't want to be passing back information all the time, because an error that occurs 6 layers down will be very hard to manage (which is why you want to start using exceptions more).
In regards to your structure - I'd break it down by domain/module. For example, have a Users folder that has all the validation, services, models and repositories specific to users.
Whenever you create or delete or save a user - fire an event, then let other modules deal with related data/content.
Hope that helps!
Sign in to participate in this thread!
The Laravel portal for problem solving, knowledge sharing and community building.
The community