presenta

Repository Pattern e Service Layer, come sono rimasto fulminato sulla via di Damasco

Dal pattern MVC al Repository Pattern e Service Layer

Filippo Matteo Riggio

Filippo Matteo Riggio

Technical Project Manager @

Previously:

- CTO / Co.Founder @ Kaleidoscope

- Fullstack Freelance Dev

- Team Leader @ Chroma /  London

- Founder di

In principio fu l'MVC

(alias come iniziare lo sviluppo di web app for dummies)

MVC Pattern

Il caso reale

(alias come l'ho presa sui denti a voler semplificare troppo)

Macro funzionalità

L’applicativo ha:

  • Una serie di script di import / export dei dati verso Dynamics 365
  • Una serie di api json che leggono / scrivono dati

62.574 linee di codice

Il cliente inizia a chiedere CR

(faccio 2 etti di notifiche, che faccio lascio?)

Il cliente richiede nuove funzionalità:

- Sincronie e integrazioni con sistemi esterni (Cerved, OneSignal, Mailgun)

- Notifiche push / email / ecc.

- Tutto tramite Comandi ed Observer di Laravel

187.724 linee di codice

Il cliente chiede altre CR

(...e l’ecommerce con il PIM

e integra l’ERP ...

che al mercato mio padre comprò…)

701.105 linee di codice

Il cliente chiede ancora altre CR

(...e l'app per i clienti

e la piattaforma eventi

che al mercato mio padre comprò…)

1.189.829 linee di codice

Come poteva finire?

Dove si trova la logica applicativa?

(alias dove sta davvero il problema?)

Dove si trova la logica applicativa?

(alias dove sta davvero il problema?)

Dove si trova la logica applicativa?

(alias dove sta davvero il problema?)

Dove si trova la logica applicativa?

(alias dove sta davvero il problema?)

Dove si trova la logica applicativa?

(alias dove sta davvero il problema?)

Dove si trova la logica applicativa?

(alias dove sta davvero il problema?)

Ed ecco che arriva il Service Layer

Service Layer

MVC Ideale VS MVC Reale VS Service Layer

Service Layer - Quando applicarlo?

You probably don't need a Service Layer

if your application's business logic will only have one kind of client

- say, a user interface - and it's use case responses don't involve multiple transactional resources. […]

 

But as soon as you envision a second kind of client, or a second transactional resource in use case responses, it pays to design in a Service Layer from the beginning.

 

- Patterns of Enterprise Architecture, Martin Fowler

Service Layer - un esempio in Laravel

<?php

namespace App\Http\Controllers\Api;

use Illuminate\Routing\Controller as BaseController;

use App\Models\Coupon;

class CouponController extends BaseController {

    public function store(CouponRequest $request)
    {
        // Validation in the custom request

        $coupon = Coupon::create($request->all());

        return response()->json(['coupon' => $coupon]);
    }

}

Service Layer - un esempio in Laravel

<?php

namespace App\Services;

use Illuminate\Database\Eloquent\Model;

use App\Models\Coupon;

class CouponService {

    public function store(array $data): Model
    {
        // TODO: Validation

        $coupon = Coupon::create($data);

        return $coupon;
    }

}

Service Layer - un esempio in Laravel

<?php

namespace App\Http\Controllers\Api;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller as BaseController;

use App\Services\CouponService;

class CouponController extends BaseController {

    public function store(Request $request)
    {
        $couponService = app()->make(CouponService::class);
        $coupon        = $couponService->store( $request->all() );

        return response()->json(['coupon' => $coupon]);
    }

}

Service Layer - un esempio in Laravel

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use App\Services\CouponService;

class ImportCoupon extends Command {

    public function handle()
    {
        $data = [...]; // IMPORT FROM CSV || IMPORT FROM EXTERNAL DATA SOURCE

        if ( $data && count($data) > 0 ) {

            $couponService = app()->make(CouponService::class);

            foreach ( $data as $item ) { 
                $coupon = $couponService->store( $item );
            }
        }

        return;
    }

}

Service Layer Yahoooo!!

Ho unificato la logica, senza avere codice ripetuto.

E la manutenzione sul lungo periodo is a breeezeee…

Service Layer - Quando NON usarlo

1. Dimensione dell'applicativo non ha senso

2. Durata del progetto è breve (non c'è manutenzione, ecc.)

3. Budget basso

Pro
- Business Logic centralizzata
- Manutenzione comoda
- Testabilità!

Un buon punto di partenza: https://github.com/zendaemon/service-layer

Contro
- Verboso
- Dimensione dell'applicativo crescono
- Budget

Credo in un solo ORM

(alias come pensavo fosse facile la vita coi DB)

Svariati ORM

Object-Relational Mapper

Non va inserita

Data Business Logic

Un esempio reale

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Company extends Model {

    protected $fillable = [...];

    public static function getCompaniesByCoords( float $lat, float $lng, int $zoom, array $filters ): Collection
    {
        //[...]
    }

}

Altro problema: Accoppiamento con la tecnologia di persistenza

Ed ecco che arriva

il Repository Pattern in soccorso!

Che cosa è un repository?

Repositories are classes or components that encapsulate the logic required to access data sources.

 

They centralize common data access functionality, providing better maintainability and decoupling the infrastructure or technology used to access databases from the domain model layer.

 

- Patterns of Enterprise Architecture, Martin Fowler

<?php

namespace App\Repositories;

use Illuminate\Database\Eloquent\Model;

use App\Models\Company;

class CompanyRepository extends BaseRepository {

    public function model()
    {
        return Company::class;
    }

    public function getCompaniesByCoords( float $lat, float $lng, int $zoom, array $filters ): Collection
    {
       
        $columns = [...];

        return $this->model->select($columns)
                           ->havingRaw('distance < ?', [ (40000 / pow(2, $zoom)) *2 ])
                           ->orderBy('distance', 'ASC');
    }

}

Repository - un esempio in Laravel

<?php

namespace App\Services;

use App\Repository\CompanyRepository;

class CompanyService {

    protected $repository;

    public function __construct()
    {
        $this->repository = app()->make(CompanyRepository::class);
    }

    public function getCompaniesByCoords( float $lat, float $lng, int $zoom, array $filters ): Collection
    {
        $companies = $this->repository->getCompaniesByCoords($lat, $lng, $zoom, $filters);
       
        // DO SOME LOGIC WITH $companies

        return $companies;
    }

}

Repository e Service

MVC Ideal VS MVC Reale VS Service Layer VS Service Layer + Repository Pattern

Repository, yeppa!!

Repository Pattern - In Conclusione

Pro
- Comodità nel cambio di layer di persistenza
- Centralizzare query complesse
- Applicare un layer di caching
- Applicare transformer layer al dato
- Applicare presentation layer al dato
- Testabilità!

Un buon punto di partenza: https://github.com/andersao/l5-repository

Contro
- Verboso
- Dimensione dell'applicativo crescono
- Budget

Bibliografia

  • Patterns of Enterprise Application Architecture - Martin Fowler

  • Clean Architecture - Robert C. Martin

 

Come finiva la storia?

Question Time

Repository Pattern e Service Layer, come sono rimasto fulminato sulla via di Damasco

By Filippo Matteo Riggio

Repository Pattern e Service Layer, come sono rimasto fulminato sulla via di Damasco

Nello sviluppo di applicativi, capita spesso di utilizzare il pattern MVC (probabilmente il pattern più conosciuto). Al crescere delle dimensioni, però, questo pattern risulta "stretto"; con limiti di architettura all'aumentare del numero di funzionalità. Con il repository pattern ed il service layer, molte delle problematiche legate al pattern MVC spariscono; rendendo agevole e consistente lo sviluppo e la manutenibilità degli applicativi. Il tutto verrà illustrato attraverso un progetto reale in PHP / Laravel.

  • 171