Co boli MVC

Prowadziliśmy swego czasu dyskusje o logice biznesowej i szukaliśmy rozwiązania starając się znaleźć jej miejsce w MVC. Stosując bowiem ten paradygmat logika naturalnie "rozmywa się" po kontrolerach i modelach (ekstremiści wstawiają ją nawet do widoków). Typowy przykład złego podejścia to kiepskie implementacje ORM (Propel-PHP, DjangoORM - Python):

class User(Model):
   def save(..):
      ...

W tym przypadku Model jest jednocześnie persistance managerem i wykonuje operacje, których nie powinien. Prawidłowe podejście to

class UserManager:
   def save(self, user):
       ....

Który kod łatwiej przetestować? Gdzie znajduje się logika zapisu stanu encji?

Inny przykład:

class Product(Model):
    def calculate_tax(self, tax):
       ...

vs

class TaxCalculator:
    def calculate_tax(self, product):
       ...

Jak widać w drugim podejściu to TaxCalculator zawiera logikę obliczania podatku dla produktu. Możemy ją łatwiej przetestować (bez uruchamiania całego środowiska i persistance) oraz możemy ją wymieniać wprowadzając różne kalkulatory, np. USATaxCalculator, VATTaxCalculator, etc.

Frameworki narzucają konwencje, ale nie ograniczają aż tak naszych działań. Dlatego w moich projektach, ostatnio opartych o Django, stosuję minimalny kod kontrolerów, operacje zamykam w formularzach (mimo, że nie są renderowane) lub funkcjach/metodach wykonujących proces na rzecz jakiegoś obiektu. W ten sposób modele są pozbawione metod będących operacjami na ich samych. Taki kod jest przede wszystkim łatwiej testowalny (mowa o unit tests), oraz nie wymaga uruchamiania i konfigurowania całego środowiska, co czasem bywa problematyczne i jest wolniejsze.

Jeśli już mowa o Django, to wyraźnie widać, gdzie popełniono błędy projektowe. Model już na wstępie posiada dwie operacje, których nie powinien mieć - model.save() i model.delete(). Zapis i odczyt stanu oraz usuwanie modelu powinny być wykonywane przez obiekt zewnętrzny (konkretnie persistence manager). Z kolei formularze są ściśle związane z widgetami HTML, co przy stosowaniu ich jako obiektów utility (np. do walidacji danych) pociąga za sobą niechciane zależności.

Warto zastosować się do kilku reguł:

  • ograniczyć ciało kontrolerów do wywoływania operacji sprowadzając je do roli przetwarzania żądań i generowania odpowiedzi; nie implementować w nich logiki biznesowej (tj. use cases/sekwencji)
  • każdy use case powinien mieć swoją implementację w oddzielnym bycie, co wyraźnie odseparuje warstwę logiki biznesowej
  • nie implementować operacji w klasach modeli, które zawierają logikę biznesową; tj. ograniczać do operacji modyfikujących lub odczytujących stan (w znakomitej większości pozostaną akcesory i mutatory lub po prostu properties)

Ciekawe artykuły:

Krótko o mnie

Jestem programistą i architektem systemów IT. Specjalizuję się w aplikacjach webowych, szczególnie w Python oraz Django, PostgreSQL oraz systemach wyszukiwana ElasticSearch.

Zajmuję się wsparciem istniejących systemów oraz projektowaniem i produkcją. W branży działam od 2001 roku. Oferuję doświadczenie, profesjonalizm oraz indywidualne podejście do zleceń.

Zainteresowanych moimi usługami zapraszam do wysłania zapytania.

Javascript logo PostgreSQL logo Cassandra logo Redis logo ElasticSearch logo Ansible logo HTML5 logo CSS3 logo NGINX logo Docker logo

Komentarze

Brak komentarzy