Модулност и Web програмиране със Scala

Функционалните програми се състоят в голяма степен от чисти функции без състояние

Как да ги организираме в нашия код?

Чистите функции често рефирират към други конкретни функции.

В някои случаи, особено при работа с конкретен домейн, е доста вероятно да искаме да се абстрахираме от конкретната имплементация на функциите, от които зависим

Във ФП това може да постигнем като функцията се подава като параметър:

Dependency injection

Този подход наричаме със сложното име “Dependency injection”

Представлява вид inversion of control – поради това, че функцията вече не създава/реферира изрично конкретна зависимост, ами я приема като параметър

Dependency injection с ООП модулност

  • Този подход работи добре, но може да направи функциите доста сложни
  • В Scala можем да използваме ООП класовете и trait-овете за да решим това

Dependency injection с ООП модулност

Dependency injection с ООП модулност

Защо dependency injection?

  • less coupling, more flexibility
  • не сме зависими от конкретна имплементация (която може да се промени)
  • позволява тестване – подменяме зависимостите със специално подбрани инстанции
  • става ясно от какво всъщност зависи определен компонент

Dependency injection – кой навързва зависимостите?

  • Runtime – популярно в Java света (Guice, Spring). Не бива валидирано по време на компилация
  • Compile-time

Compile-time dependency injection – демо

Thin cake pattern

Runtime конфигурация

Как да определим кои са ни модулите?

  • инфраструктурни/библиотечни – занимаващи се с конкретна библиотека или конкретна част от инфраструктурата на приложението (конфигурация и помощни функции за базата, за HTTP комуникация и т.н.)
  • домейн модули – за всеки домейн/поддомейн

Как да определим кои са ни модулите?

  • Накрая ще разгледаме приложение за уеб магазин
  • Неговият домейн може да бъде разделен на следните поддомейни, всеки от тях различен модул:
    • Управление на потребителите (регистрация, информация за потребителите, аутентикация)
    • Инвентар (продукти, наличност и т.н.)
    • Магазин и поръчки
  • Това разделение е естествено и обикновено позволява една промяна да засегне само един модул. Също така позволява по-лесно евентулно отделяне на модулите

Как да определим кои са ни модулите?

В някои проекти в индустрията ще срещнете разделение по слоеве – model, repository, service, controller, и т.н. Това почти винаги не работи добре, тъй като една промяна започва да засяга много пакети/модули. Също така компонентите, които имат строга връзка помежду си, остават разделечени и губят своята локалност, което прави кодът по-труден за проследяване.

Разделянето по слоеве не трябва да е основно

Повече информация

DI in Scala guide

Изразителност на функционалното програмиране

  • Основна полза на функционалното програмиране е възможността за абстрактност
  • Но най-силно изразителността му се проличава когато добавим конкретика и опитаме да моделираме конкретен домейн
  • Често създаваме конкретни функционални DSL/библиотека за определен домейн
  • Но много техни аспекти моделираме срещу познати ни абстракции, като моноид, монада, апликатив и т.н.

Домейн: Web и HTTP

HTTP библиотеки за Scala (с допълнения)

Play Framework

Play Framework – добавяне към проекта

Play Framework – стартиране

  • sbt run – стартира се в development режим на http://localhost:9000. При отваряне, ако има нужда, приложението автоматично се рекомпилира и рестартира
  • sbt runProd – стартиране в production режим
  • sbt stage – пакетиране на приложението, така че да може да бъде стартирано самостоятелно

HTTP app

Action

Обработва request до HTTP response

Action

  • Това е най-общият и най-сложният вид

Action

Play Framework предоставя по-лесен за употреба DSL за често срещаните обработки

  • синхронно генериране на резултат от request:

    Action(Request[AnyContent] => Response)
  • асинхронно генериране на резултат от request:

    Action.async(Request[AnyContent] => Future[Response])
  • парсване на тялото на request-а:

    Action(parse.text)(Request[String] => Response)
  • комбинация:

    Action.async(parse.text)(Request[String] => Future[Response])

HTTP app

Json

Body parsing

Shopping App