Ik heb controllers zoals CustomerActivityController, en ik heb een service laag met services zoals CustomerActivityService. Stel dat ik een nieuwe customer activity wil maken via een API call in JSON format. De controller ontvangt een JSON string als request data, en de JSON wordt geparsed naar een array.
array(
'user_id' => 9,
'customer_id' => 123,
'activity' => 'Made a phone call',
'date' => '...'
)Dus, mijn eerste stap (in controller) is het converteren van het input format (JSON of iets anders in de toekomst) naar universele data zoals een array.
Dan valideer ik de waardes in de array, gebruikmakend van Illuminate\Support\Facades\Validator.
In mijn controller method ziet dit er uit als:
<?
$dataArray = \json_decode($request->input('json'), true);
$validator = Validator::make($dataArray, [
'user_id' => 'required|integer|gt:0',
'customer_id' => 'required|integer|gt:0',
'activity' => 'required|string',
'date' => 'required|date'
]);
if($validator->fails()) {
// ... error response.
}
// Input beschouwd als valide, ga verder...
$this->_customerActivityService->createActivity($dataArray);
?>
Ik doe het overal op die manier.
Op de regel
$this->_customerActivityService->createActivity($dataArray); Is $dataArray eigenlijk nog niet écht 'valid', omdat user_id of customer_id nog ongeldig kunnen zijn (id's bestaan misschien niet).
Ik zie dit als een aparte soort validatie, omdat dit niet gewoon rauwe data controleren is, maar meer richting de business logic gaat en samenhangt met andere data. Naar mijn mening zou dit dus zéker in een service layer moeten gebeuren, en in elk geval niet rechtstreeks in de controller.
Wat ik mezelf afvraag:
Ik heb nu validatie op verschillende plekken (in controller, en de businessdata-validatie in service). Ik zou de rauwe-data validatie gewoon kunnen verplaatsen naar mijn service class, maar dan is de service class 'tightly coupled' aan het request format (omdat de service dan een Request object of JSON string zou moeten krijgen als method parameters). Mijn service moet herbruikbaar zijn voor elk request type. (Dus dat ik verschillende controllers heb voor Web/API/..., maar dezelfde service class).
Ik vraag me dus af hoe ik de gegevens het beste kan doorgeven aan de service laag.
Een aantal opties die ik me kan bedenken:
1. Gewoon een array als input in de service accepteren:
$this->_customerActivityService->createActivity($dataArray); Dit heeft echter het nadeel dat de array de correcte keys moet hebben en geen segment mag missen (de programmeur die mijn service class aanroept moet de samenstelling van de data array weten). Hiervoor moet je dus de klassedocumentatie raadplegen of de code van de service lezen.
2. Verschillende parameters voor elke 'eigenschap' van de data.
$this->_customerActivityService->createActivity($userId, $customerId, $activity, $date) Voordeel: method signature is beschrijvend. Door de parameters is het meteen duidelijk wat de method nodig heeft. Nadeel: de parameter lijst kan extreem lang worden bij grotere data structuren, en je kunt op zich geen hierarchische data verwerken.
3. Accepteer een data object.
$this->_customerActivityService->createActivity($activityInstance);Dan maak ik me nog zorgen over het volgende:
Aangezien de rauwe-data validatie in de controller gebeurt, en de service doet de 'logic' validatie, komt het er op neer dat de service class zijn input 'vertrouwt'. De service class gaat er van uit dat de data die hij ontvangt al is gevalideerd, maar de service zou per ongeluk kunnen worden aangeroepen met input data die niet (juist) is gevalideerd. Zoals -12 doorgeven als $user_id in een service method call. Vandaar dus dat ik de neiging krijg om gewoon alle validatie (dus ook raw-data validatie) in de service class te plaatsen.
Natuurlijk moet de collega die de service class aanroept weten wat hij doet, maar bij OOP design is het ook juist de bedoeling dat je het open-closed principe toepast en samenhangende logica zo dicht mogelijk bij elkaar plaatst.
Heeft er iemand ervaring met deze hardcore OOP, en MVC architectuur?