SlideShare uma empresa Scribd logo
1 de 105
Baixar para ler offline
Acercándonos a la Programación Funcional 🦄
a través de la Arquitectura Hexagonal 🎯
Habla Computing + CodelyTV
Madrid, 21 de Noviembre de 2018
Functional Programming intro based on Hexagonal Architecture
Who we are
@JavierCane@juanshac
Session goal
🤔

Rethinking time
Functional Programming intro based on Hexagonal Architecture
Reviewing different technics to solve the same problems with more modularity
🗺Design principles
%Implementation details
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
Functional Programming intro based on Hexagonal Architecture
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
🏗
APIs
⚡
Errors
🏗
APIs
⚡
Errors
🗺Design principles
%Implementation details
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
Functional Programming intro based on Hexagonal Architecture
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
🏗
APIs
⚡
Errors
🏗
APIs
⚡
Errors
🗺 Design principles > 🎯 Hexagonal Architecture
🛀 Clean Architectures
🤯 Ports & adapters
🚫
🗺 Design principles > 🎯 Hexagonal Architecture
Layers & dependency rule
Infrastructure
Application
Domain
🗺 Design principles > 🎯 Hexagonal Architecture
Layered Architecture
Presentation
Domain
Database
🗺 Design principles > 🎯 Hexagonal Architecture
Layered vs Hexagonal
Presentation
Domain
Database
Infrastructure
Application
Domain
🗺 Design principles > 🎯 Hexagonal Architecture
CONTROLLER
REPOS
MODELS
SERVICES
APPLICATION SERVICE
D
A
I
Request flow
IMPLEMENTATION
Acceptance test
Unit test Integration test
🗺 Design principles > 🎯 Hexagonal Architecture
Acceptance test
Unit test Integration test
CONTROLLER
REPOS
MODELS
SERVICES
APPLICATION SERVICE
D
A
I
Request flow
IMPLEMENTATION
🗺 Design principles > 🎯 Hexagonal Architecture
Port
Adapter
VideoPostController
VideoRepository
VideoVideoCreator
D
A
I
DoobieMySqlVideoRepo
Acceptance test
Unit test Integration test
Request flow example
🗺 Design principles > 🎯 Hexagonal Architecture
VideoPostController
VideoRepository
VideoVideoCreator
D
A
I
DoobieMySqlVideoRepo
Acceptance test
Unit test Integration test
Port
Adapter
🗺 Design principles > 🎯 Hexagonal Architecture
Request flow example
Domain Events, CQRS, and Event Sourcing as an optional further step
🗺Design principles
%Implementation details
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
Functional Programming intro based on Hexagonal Architecture
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
🏗
APIs
⚡
Errors
🏗
APIs
⚡
Errors
🗺Design principles
%Implementation details
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
Functional Programming intro based on Hexagonal Architecture
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
🏗
APIs
⚡
Errors
🏗
APIs
⚡
Errors
What is a functional architecture?
DSL1
DSL2
DSLN
…
🗺 Design principles > 🦄 Functional Architectures
A FUNCTIONAL LANGUAGE IS A DOMAIN-
SPECIFIC LANGUAGE FOR DEFINING
DOMAIN-SPECIFIC LANGUAGES
The DSLs of your application
• Infrastructure DSLs
• HTTP
• Databases
• Messaging
• Application-specific DSLs
• Use cases
• Repos
• …
HTTP
SERVICE DSL
SQL
REPO DSL
🗺 Design principles > 🦄 Functional Architectures
Are hexagonal and functional architectures like apples and oranges?
HTTP
SERVICE DSL
SQL
REPO DSL
CONTROLLER
SERVICE IMPL.
ORM
DB
ADAPTER/INTERP.
PORT/DSL
🗺 Design principles > 🦄 Functional Architectures
There are no exceptions • Everything is either a port or an adapter
• Every adapter implements a given port
• Adapters are implemented on top of other ports
VideoPostController
VideoRepositoryVideoRepoC
DoobieMySqlVideoRepo
VideoCreator
HTTP
🗺 Design principles > 🦄 Functional Architectures
🎯
🦄
VideoPostController
VideoRepository
VideoCreator
DoobieMySqlVideoRepo
VideoPostController
VideoRepositoryVideoRepoC
DoobieMySqlVideoRepo
VideoCreator
HTTP
🗺 Design principles > 🦄 Functional Architectures
…
VideoPostController
VideoRepoC
VideoCreator
HTTPVideoPostClient
VideoCreator…
……
Why to declare the Application Service API?
🤔 Rethinking time
🗺 Design principles
🗺 Design principles > 🤔 Rethinking time
Final goals
• Decouple from infrastructure
• Testing easiness (test doubles when testing out use cases)
• Change tolerance
• ¡Same goals!
🗺 Design principles > 🤔 Rethinking time
Differences: More decoupling and indirection levels
• Controllers:
• Consistency (APIs for all DSLs) vs. Specific needs
• Conclusion: We need industry standards. PHP-FIG as example to follow
• Application Services:
• Consistency (APIs for all DSLs) vs. Specific needs
• Reuse use case APIs in clients (e.g. user clients)
🗺Design principles
%Implementation details
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
Functional Programming intro based on Hexagonal Architecture
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
🏗
APIs
⚡
Errors
🏗
APIs
⚡
Errors
🗺Design principles
%Implementation details
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
Functional Programming intro based on Hexagonal Architecture
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
🏗
APIs
⚡
Errors
🏗
APIs
⚡
Errors
Remembering the Architecture layers
VideoPostController VideoRepositoryVideoCreator
DoctrineMySqlVideoRepo
App
Dom
Infra
🐘 PHP
final class VideoPostController extends ApiController
{
private $creator;
public function __construct(VideoCreator $creator)
{
parent::__construct();
$this->creator = $creator;
}
public function __invoke(Request $request)
{
VideoPostController - HTTP API Controller
VideoPostController VideoRepositoryVideoCreator
DoctrineMySqlVideoRepo
final class VideoPostController extends ApiController
{
private $creator;
public function __construct(VideoCreator $creator)
{
parent::__construct();
$this->creator = $creator;
}
public function __invoke(Request $request)
{
VideoPostController - HTTP API Controller
VideoPostController VideoRepositoryVideoCreator
DoctrineMySqlVideoRepo
final class VideoPostController extends ApiController
{
private $creator;
public function __construct(VideoCreator $creator)
{
parent::__construct();
$this->creator = $creator;
}
public function __invoke(Request $request)
{
$id = new VideoId($request->get('id'));
$type = new VideoType($request->get('type'));
$title = new VideoTitle($request->get('title'));
$url = new VideoUrl($request->get('url'));
$courseId = new CourseId($request->get('course_id'));
$this->creator->create(
$id, $type, $title, $url, $courseId
);
return new ApiHttpCreatedResponse();
VideoPostController - HTTP API Controller
VideoPostController VideoRepositoryVideoCreator
DoctrineMySqlVideoRepo
final class VideoPostController extends ApiController
{
private $creator;
public function __construct(VideoCreator $creator)
{
parent::__construct();
$this->creator = $creator;
}
public function __invoke(Request $request)
{
$id = new VideoId($request->get('id'));
$type = new VideoType($request->get('type'));
$title = new VideoTitle($request->get('title'));
$url = new VideoUrl($request->get('url'));
$courseId = new CourseId($request->get('course_id'));
$this->creator->create(
$id, $type, $title, $url, $courseId
);
return new ApiHttpCreatedResponse();
VideoPostController - HTTP API Controller
VideoPostController VideoRepositoryVideoCreator
DoctrineMySqlVideoRepo
final class VideoCreator
{
private $repository;
private $publisher;
public function __construct(
VideoRepository $repository,
DomainEventPublisher $publisher
) {
$this->repository = $repository;
$this->publisher = $publisher;
}
public function create(
VideoCreator - Application Service/Use case
VideoPostController VideoRepositoryVideoCreator
DoobieMySqlVideoRepoDoctrineMySqlVideoRepo
final class VideoCreator
{
private $repository;
private $publisher;
public function __construct(
VideoRepository $repository,
DomainEventPublisher $publisher
) {
$this->repository = $repository;
$this->publisher = $publisher;
}
public function create(
VideoCreator - Application Service/Use case
VideoPostController VideoRepositoryVideoCreator
DoctrineMySqlVideoRepo
private $repository;
private $publisher;
public function __construct(
VideoRepository $repository,
DomainEventPublisher $publisher
) {
$this->repository = $repository;
$this->publisher = $publisher;
}
public function create(
VideoId $id, VideoType $type, VideoTitle $title,
VideoUrl $url, CourseId $courseId
) {
$video = Video::create($id, $type, $title, $url, $courseId);
$this->repository->save($video);
$this->publisher->publish(...$video->pullDomainEvents());
}
}
VideoCreator - Application Service/Use case
VideoPostController VideoRepositoryVideoCreator
DoctrineMySqlVideoRepo
private $repository;
private $publisher;
public function __construct(
VideoRepository $repository,
DomainEventPublisher $publisher
) {
$this->repository = $repository;
$this->publisher = $publisher;
}
public function create(
VideoId $id, VideoType $type, VideoTitle $title,
VideoUrl $url, CourseId $courseId
) {
$video = Video::create($id, $type, $title, $url, $courseId);
$this->repository->save($video);
$this->publisher->publish(...$video->pullDomainEvents());
}
}
VideoCreator - Application Service/Use case
VideoPostController VideoRepositoryVideoCreator
DoctrineMySqlVideoRepo
public static function create(
VideoId $id,
VideoType $type,
VideoTitle $title,
VideoUrl $url,
CourseId $courseId
): Video {
$video = new self($id, $type, $title, $url, $courseId);
$video->record(
new VideoCreatedDomainEvent(
$id->value(),
[
'type' => $type->value(),
'title' => $title->value(),
'url' => $url->value(),
'courseId' => $courseId->value(),
]
)
);
return $video;
Video#create - AggregateRoot
public static function create(
VideoId $id,
VideoType $type,
VideoTitle $title,
VideoUrl $url,
CourseId $courseId
): Video {
$video = new self($id, $type, $title, $url, $courseId);
$video->record(
new VideoCreatedDomainEvent(
$id->value(),
[
'type' => $type->value(),
'title' => $title->value(),
'url' => $url->value(),
'courseId' => $courseId->value(),
]
)
);
return $video;
Video#create - AggregateRoot
private $repository;
private $publisher;
public function __construct(
VideoRepository $repository,
DomainEventPublisher $publisher
) {
$this->repository = $repository;
$this->publisher = $publisher;
}
public function create(
VideoId $id, VideoType $type, VideoTitle $title,
VideoUrl $url, CourseId $courseId
) {
$video = Video::create($id, $type, $title, $url, $courseId);
$this->repository->save($video);
$this->publisher->publish(...$video->pullDomainEvents());
}
}
VideoCreator - Application Service/Use case
VideoPostController VideoRepositoryVideoCreator
DoctrineMySqlVideoRepo
interface VideoRepository
{
public function save(Video $video): void;
public function search(VideoId $id): ?Video;
public function searchByCriteria(Criteria $criteria): Videos;
}
VideoRepository - Domain contract/Port
VideoPostController VideoRepositoryVideoCreator
DoctrineMySqlVideoRepo
DoobieMySqlVideoRepository - Infrastructure implementation/Adapter
VideoPostController VideoRepositoryVideoCreator
final class DoctrineMySqlVideoRepository implements VideoRepository
{
private $entityManager;
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function save(Video $video): void
{
$this->entityManager()->persist($entity);
DoctrineMySqlVideoRepo
final class DoctrineMySqlVideoRepository implements VideoRepository
{
private $entityManager;
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function save(Video $video): void
{
$this->entityManager()->persist($entity);
$this->entityManager()->flush($entity);
}
// ...
}
DoobieMySqlVideoRepository - Infrastructure implementation/Adapter
VideoPostController VideoRepositoryVideoCreator
DoctrineMySqlVideoRepo
final class DoctrineMySqlVideoRepository implements VideoRepository
{
private $entityManager;
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function save(Video $video): void
{
$this->entityManager()->persist($entity);
$this->entityManager()->flush($entity);
}
// ...
}
DoobieMySqlVideoRepository - Infrastructure implementation/Adapter
VideoPostController VideoRepositoryVideoCreator
DoctrineMySqlVideoRepo
🧖 Scala
final class VideoPostController(creator: VideoCreator) {
def post(id: String, title: String): StandardRoute = {
creator.create(VideoId(id), VideoTitle(title))
complete(HttpResponse(Created))
}
}
VideoPostController - HTTP API Controller
VideoPostController VideoRepositoryVideoCreator
DoobieMySqlVideoRepo
final class VideoPostController(creator: VideoCreator) {
def post(id: String, title: String): StandardRoute = {
creator.create(VideoId(id), VideoTitle(title))
complete(HttpResponse(Created))
}
}
VideoPostController - HTTP API Controller
VideoPostController VideoRepositoryVideoCreator
DoobieMySqlVideoRepo
final class VideoPostController(creator: VideoCreator) {
def post(id: String, title: String): StandardRoute = {
creator.create(VideoId(id), VideoTitle(title))
complete(HttpResponse(Created))
}
}
VideoPostController - HTTP API Controller
VideoPostController VideoRepositoryVideoCreator
DoobieMySqlVideoRepo
object VideoQuality {
val maxQuality = 50
val minQuality = 0
}
final case class VideoQuality(value: Int) {
require(value <= maxQuality, s"Video quality value greater than $maxQuality")
require(value >= minQuality, s"Video quality value less than $minQuality")
def increase(): VideoQuality = {
if (value >= maxQuality) this
else copy(value + 1)
}
}
OOP: Data+Behaviour
👤 Domain models > 🎯 OOP > VideoQuality Value Object
object VideoQuality {
val maxQuality = 50
val minQuality = 0
}
final case class VideoQuality(value: Int) {
require(value <= maxQuality, s"Video quality value greater than $maxQuality")
require(value >= minQuality, s"Video quality value less than $minQuality")
def increase(): VideoQuality = {
if (value >= maxQuality) this
else copy(value + 1)
}
}
Behaviour
Simpler API
+ domain semantics
+ immutability
👤 Domain models > 🎯 OOP > VideoQuality Value Object
case class Video(
id: VideoId,
title: VideoTitle,
quality: VideoQuality
) {
def increaseQuality(): Video =
copy(quality = quality.increase())
}
VideoQualityIncreaser Video
increaseQuality()
Behaviour
Rich domain model - Tell don’t ask
👤 Domain models > 🎯 OOP > Video Entity
final class VideoPostController(creator: VideoCreator) {
def post(id: String, title: String): StandardRoute = {
creator.create(VideoId(id), VideoTitle(title))
complete(HttpResponse(Created))
}
}
VideoPostController - HTTP API Controller
VideoPostController VideoRepositoryVideoCreator
DoobieMySqlVideoRepo
final class VideoPostController(creator: VideoCreator) {
def post(id: String, title: String): StandardRoute = {
creator.create(VideoId(id), VideoTitle(title))
complete(HttpResponse(Created))
}
}
VideoPostController - HTTP API Controller
VideoPostController VideoRepositoryVideoCreator
DoobieMySqlVideoRepo
final class VideoCreator(
repository: VideoRepository,
publisher: MessagePublisher
) {
def create(id: VideoId, title: VideoTitle): Unit = {
val video = Video(id, title)
repository.save(video)
publisher.publish(VideoCreated(video))
}
}
VideoCreator - Application Service/Use case
VideoPostController VideoRepositoryVideoCreator
DoobieMySqlVideoRepo
final class VideoCreator(
repository: VideoRepository,
publisher: MessagePublisher
) {
def create(id: VideoId, title: VideoTitle): Unit = {
val video = Video(id, title)
repository.save(video)
publisher.publish(VideoCreated(video))
}
}
VideoCreator - Application Service/Use case
VideoPostController VideoRepositoryVideoCreator
DoobieMySqlVideoRepo
final class VideoCreator(
repository: VideoRepository,
publisher: MessagePublisher
) {
def create(id: VideoId, title: VideoTitle): Unit = {
val video = Video(id, title)
repository.save(video)
publisher.publish(VideoCreated(video))
}
}
VideoCreator - Application Service/Use case
VideoPostController VideoRepositoryVideoCreator
DoobieMySqlVideoRepo
trait VideoRepository {
def all(): Future[Seq[Video]]
def save(video: Video): Future[Unit]
}
VideoRepository - Domain contract/Port
VideoPostController VideoRepositoryVideoCreator
DoobieMySqlVideoRepo
DoobieMySqlVideoRepository - Infrastructure implementation/Adapter
VideoPostController VideoRepositoryVideoCreator
DoobieMySqlVideoRepo
final class DoobieMySqlVideoRepository(db: DoobieDbConnection)
(implicit ec: ExecutionContext) extends VideoRepository {
override def all(): Future[Seq[Video]] =
db.read(sql”SELECT…”.query[Video].to[Seq])
override def save(video: Video): Future[Unit] =
sql"INSERT INTO…”.update.run
.transact(db.transactor)
.unsafeToFuture().map(_ => ())
}
🗺Design principles
%Implementation details
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
Functional Programming intro based on Hexagonal Architecture
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
🏗
APIs
⚡
Errors
🏗
APIs
⚡
Errors
🗺Design principles
%Implementation details
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
Functional Programming intro based on Hexagonal Architecture
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
🏗
APIs
⚡
Errors
🏗
APIs
⚡
Errors
object VideoQuality {
val maxQuality = 50
val minQuality = 0
}
final case class VideoQuality(value: Int) {
require(value <= maxQuality, s"Video quality value greater than $maxQuality")
require(value >= minQuality, s"Video quality value less than $minQuality")
def increase(): VideoQuality = {
if (value >= maxQuality) this
else copy(value + 1)
}
}
VO: Raise exceptions
for invalid states
⚡Error Handling > 🎯 OOP > Raise exceptions
final class VideoFinder(repository: VideoRepository)
(implicit ec: ExecutionContext) {
def find(id: VideoId): Future[Video] =
repository.search(id).map { videoOption =>
if (videoOption.isEmpty) throw VideoNotFound(id)
else videoOption.get
}
}
Imperativeness
⚡Error Handling > 🎯 OOP > Raise exceptions
trait VideoRepository {
def search(id: VideoId): Future[Option[Video]]
}
final class VideoGetController extends ApiController
{
protected function exceptions(): array
{
return [
VideoNotFound::class => Response::HTTP_NOT_FOUND,
];
}
// …
}
HTTP Mapping
⚡Error Handling > 🎯 OOP > Handle exceptions
🗺Design principles
%Implementation details
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
Functional Programming intro based on Hexagonal Architecture
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
🏗
APIs
⚡
Errors
🏗
APIs
⚡
Errors
🗺Design principles
%Implementation details
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
Functional Programming intro based on Hexagonal Architecture
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
🏗
APIs
⚡
Errors
🏗
APIs
⚡
Errors
🗺 Design principles > 🦄 Functional Architectures
VideoController
VideoFinderRepo
VideoFinder
HTTPVideoClient
VideoFinder…
…
DoobieRepo…
VideoRepo
API
trait VideoRepository {
def all(): Future[Seq[Video]]
def save(video: Video):
Future[Unit]
}
Domain contract/PortVideoRepository
?
trait VideoRepository {
def all(): cats.IO[Seq[Video]]
def save(video: Video):
cats.IO[Unit]
}
trait VideoRepository {
def all(): Seq[Video]
def save(video: Video): Unit
}
trait VideoRepository {
def all(): Seq[Video]
def save(video: Video): Unit
}
trait VideoRepository {
def all(): Future[Seq[Video]]
def save(video: Video):
Future[Unit]
}
trait VideoRepository {
def all(): cats.IO[Seq[Video]]
def save(video: Video):
cats.IO[Unit]
}
Domain contract/Port
infrastructure leaks
VideoRepository
?
trait VideoRepository[P[_]] {
def all(): P[Seq[Video]]
def save(video: Video): P[Unit]
}
TYPE (CONSTRUCTOR) CLASSES
VideoRepository
?
Domain contract/Port
🗺 Design principles > 🦄 Functional Architectures
VideoController
VideoFinderRepo
VideoFinder
HTTPVideoClient
VideoFinder…
…
DoobieRepo…
VideoRepo
INSTANCE
Infrastructure implementation/Adapter
import scala.concurent.Future
final class DoobieMySqlVideoRepoFuture(
db: DoobieDbConnection[Future])(implicit
ec: ExecutionContext) extends VideoRepository[Future] {
override def all(): Future[Seq[Video]] =
db.read(sql"SELECT ...".query[Video].to[Seq])
override def save(video: Video): Future[Unit] =
sql"INSERT INTO ... ".update.run
.transact(db.transactor)
.map(_ => ())
}
VideoRepository
DoobieVideoRepoFuture
INSTANTIATION
Infrastructure implementation/Adapter
import scala.concurent.Future
final class DoobieMySqlVideoRepoFuture(
db: DoobieDbConnection[Future])(implicit
ec: ExecutionContext) extends VideoRepository[Future] {
override def all(): Future[Seq[Video]] =
db.read(sql"SELECT ...".query[Video].to[Seq])
override def save(video: Video): Future[Unit] =
sql"INSERT INTO ... ".update.run
.transact(db.transactor)
.map(_ => ())
}
VideoRepository
DoobieVideoRepoFuture
INSTANTIATION
Infrastructure implementation/AdapterVideoRepository
DoobieVideoRepoIO
import cats.effect.IO
final class DoobieMySqlVideoRepoIO(
db: DoobieDbConnection[IO]) extends VideoRepository[IO] {
override def all(): IO[Seq[Video]] =
db.read(sql"SELECT ...".query[Video].to[Seq])
override def save(video: Video): IO[Unit] =
sql"INSERT INTO ... ".update.run
.transact(db.transactor)
.map(_ => ())
}
INSTANTIATION
Infrastructure implementation/AdapterVideoRepository
DoobieVideoRepoIO
import cats.effect.IO
final class DoobieMySqlVideoRepoIO(
db: DoobieDbConnection[IO]) extends VideoRepository[IO] {
override def all(): IO[Seq[Video]] =
db.read(sql"SELECT ...".query[Video].to[Seq])
override def save(video: Video): IO[Unit] =
sql"INSERT INTO ... ".update.run
.transact(db.transactor)
.map(_ => ())
}
INSTANTIATION
Infrastructure implementation/AdapterVideoRepository
DoobieVideoRepo (GENERIC) INSTANTIATION
final case class DoobieMySqlVideoRepo[P[_]: Monad]()(
db: DoobieDbConnection[P]) extends VideoRepository[P] {
override def all(): P[Seq[Video]] =
db.read(sql"SELECT …”.query[Video].to[Seq])
override def save(video: Video): P[Unit] =
sql"INSERT …”.update.run
.transact(db.transactor)
.map(_ => ())
}
Infrastructure implementation/AdapterVideoRepository
DoobieVideoRepo (GENERIC) INSTANTIATION
final case class DoobieMySqlVideoRepo[P[_]: Monad]()(
db: DoobieDbConnection[P]) extends VideoRepository[P] {
override def all(): P[Seq[Video]] =
db.read(sql"SELECT …”.query[Video].to[Seq])
override def save(video: Video): P[Unit] =
sql"INSERT …”.update.run
.transact(db.transactor)
.map(_ => ())
}
🗺Design principles
%Implementation details
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
Functional Programming intro based on Hexagonal Architecture
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
🏗
APIs
⚡
Errors
🏗
APIs
⚡
Errors
🗺Design principles
%Implementation details
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
Functional Programming intro based on Hexagonal Architecture
🦄
Functional
Architectures
🎯
Hexagonal
Architecture
🏗
APIs
⚡
Errors
🏗
APIs
⚡
Errors
🗺 Design principles > 🦄 Functional Architectures
VideoController
VideoFinderRepo
VideoFinder
HTTPVideoClient
VideoFinder…
…
DoobieRepo…
VideoRepo
API
⚡Error Handling > 🦄 FP > Raise errors
trait VideoFinder[P[_]]{
def find(id: VideoId)(implicit
E: MonadError[P, VideoFinderError]): P[Video]
}
A functional signature doesn’t hide anything!
trait MonadError[F[_], E] {
def raiseError[A](e: E): F[A]
def handleErrorWith[A](fa: F[A])(f: E => F[A]): F[A]
}
🗺 Design principles > 🦄 Functional Architectures
VideoController
VideoFinderRepo
VideoFinder
HTTPVideoClient
VideoFinder…
…
DoobieRepo…
VideoRepo
INSTANCE / LOGIC
Imperativeness
⚡Error Handling > 🦄 FP > Raise errors
Declarativeness
+
final case class VideoFinderRepo[P[_]]()(
implicit repository: VideoRepository[P]
) extends VideoFinder[P] {
def find(id: VideoId)(implicit
E: MonadError[P, VideoFinderError]): P[Video] =
repository.search(id) flatMap { maybeVideo =>
maybeVideo.fold(E.raiseError[Video](VideoNotFound(id))){
video => video.pure[P]
}
}
}
⚡Error Handling > 🦄 FP > Raise errors
final case class VideoFinderRepo[P[_]: Monad]()(
implicit repository: VideoRepository[P]
) extends VideoFinder[P] {
def find(id: VideoId)(implicit
E: MonadError[P, VideoFinderError]): P[Video] =
repository.search(id) flatMap { maybeVideo =>
maybeVideo.fold(E.raiseError[Video](VideoNotFound(id))){
video => video.pure[P]
}
}
}
Imperativeness
⚡Error Handling > 🦄 FP > Raise errors
final case class VideoFinderRepo[P[_]: Monad]()(
implicit repository: VideoRepository[P]
) extends VideoFinder[P] {
def find(id: VideoId)(implicit
E: MonadError[P, VideoFinderError]): P[Video] =
val maybeVideo = repository.search(id) ;
if (videoOption.isEmpty) throw VideoNotFound(id)
else videoOption.get
}
Monadic programming is
imperative programming with steroids!
🗺 Design principles > 🦄 Functional Architectures
VideoController
VideoFinderRepo
VideoFinder
HTTPVideoClient
VideoFinder…
…
DoobieRepo…
VideoRepo
LOGIC
object FutureBasedController{
case class VideoController(
videoFinder: VideoFinder[Future]
)(implicit ec: ExecutionContext) extends Http4sDsl[Future] {
val service = HttpService[Future] {
case GET -> Root / "videos" / id =>
videoFinder.find(VideoId(id)) flatMap {
case video =>
Ok(video.asJson)
} recoverWith {
case error: VideoNotFound =>
NotFound(error.asJson)
}
}
}
}
⚡Error Handling > 🦄 FP > Raise errors
Future-based
HTTP Mapping
LOGIC
case class VideoController[P[_]: Effect](
videoFinder: VideoFinder[P]
) extends Http4sDsl[P] {
val service = HttpService[P] {
case GET -> Root / "videos" / id =>
videoFinder.find(VideoId(id)) flatMap {
case video =>
Ok(video.asJson)
} handleErrorWith {
case error: VideoNotFound =>
NotFound(error.asJson)
}
}
}
⚡Error Handling > 🦄 FP > Raise errors
Generic
HTTP Mapping
MonadError
API
LOGIC
🗺 Design principles > 🦄 Functional Architectures
VideoController
VideoFinderRepo
VideoFinder
HTTPVideoClient
VideoFinder…
…
DoobieRepo…
VideoRepo
INSTANCE
class HttpVideoFinder extends VideoFinder[Future] {
def find(id: VideoId)(implicit
E: MonadError[Future, VideoFinderError]): Future[Video] =
Http().singleRequest(HttpRequest(
method = HttpMethods.GET,
uri = httpConfig.addPath("videos"))).flatMap {
case HttpResponse(StatusCodes.OK, _, entity, _) =>
entity.dataBytes
.runFold(ByteString(""))(_ ++ _)
.map(_.utf8String.parseJson.convertTo[Video])
case HttpResponse(StatusCodes.NotFound, _, _, _) =>
E.raiseError(VideoFinderError(id))
}
}
⚡Error Handling > 🦄 FP > Raise errors
🤔 Rethinking time
% Implementation details
Differences on APIs/layers implementation
• Same goal: Decouple from infrastructure
• 🦄 FP:
• One step forward to avoid infrastructure leaks
• Again, through type classes: a better API
• More complexity: monads, applicatives, etc.
% Implementation > 🤔 Rethinking time > 🏗 APIs
Differences on error handling
• 🦄 FP:
• FP tell no lies (or, rather, doesn’t hide anything): more robust contracts using
type system
• Declarative: more abstract about how the error will be raised (exceptions,
Option, Either, etc)
% Implementation > 🤔 Rethinking time > ⚡Error Handling
🍕Takeaways
🎯
🦄
VideoPostController
VideoRepository
VideoCreator
DoobieMySqlVideoRepo
VideoPostController
VideoRepositoryVideoRepoC
DoobieMySqlVideoRepo
VideoCreator
HTTP
🍕Takeaways
⚡ Exceptions
🏗 Monolithic interfaces
👤 Rich domain models
🍕Takeaways
⚡ Exceptions
🏗 Monolithic interfaces
👤 Rich domain models
🦄 TypeClasses
⚛ APIs with steroids
🧐 FP as a subsuming paradigm
ℹ More info
References
ℹ More info
👉Formación FP presencial 👈
hablapps.com
👉Cursos en vídeo online 👈
Descuento asistentes charla: Primer mes 19€: bit.ly/codelytv19e
4 Questions?

Mais conteúdo relacionado

Mais procurados

Clean Architecture
Clean ArchitectureClean Architecture
Clean ArchitectureBadoo
 
Domain Driven Design
Domain Driven DesignDomain Driven Design
Domain Driven DesignRyan Riley
 
Implementing DDD Concepts in PHP
Implementing DDD Concepts in PHPImplementing DDD Concepts in PHP
Implementing DDD Concepts in PHPSteve Rhoades
 
Clean Architecture Essentials - Stockholm Software Craftsmanship
Clean Architecture Essentials - Stockholm Software CraftsmanshipClean Architecture Essentials - Stockholm Software Craftsmanship
Clean Architecture Essentials - Stockholm Software CraftsmanshipIvan Paulovich
 
Domain Driven Design(DDD) Presentation
Domain Driven Design(DDD) PresentationDomain Driven Design(DDD) Presentation
Domain Driven Design(DDD) PresentationOğuzhan Soykan
 
Introducing Domain Driven Design - codemash
Introducing Domain Driven Design - codemashIntroducing Domain Driven Design - codemash
Introducing Domain Driven Design - codemashSteven Smith
 
Deep dive into swift UI
Deep dive into swift UIDeep dive into swift UI
Deep dive into swift UIOsamaGamal26
 
Hexagonal architecture - message-oriented software design
Hexagonal architecture  - message-oriented software designHexagonal architecture  - message-oriented software design
Hexagonal architecture - message-oriented software designMatthias Noback
 
Mucon 2019: OOps I DDD it again and again
Mucon 2019: OOps I DDD it again and againMucon 2019: OOps I DDD it again and again
Mucon 2019: OOps I DDD it again and againOra Egozi-Barzilai
 
Brownfield Domain Driven Design
Brownfield Domain Driven DesignBrownfield Domain Driven Design
Brownfield Domain Driven DesignNicolò Pignatelli
 
AxonIQCon22 - Beyond DDD 101 - Zambrovski-Galinski.pptx
AxonIQCon22 - Beyond DDD 101 - Zambrovski-Galinski.pptxAxonIQCon22 - Beyond DDD 101 - Zambrovski-Galinski.pptx
AxonIQCon22 - Beyond DDD 101 - Zambrovski-Galinski.pptxJan Galinski
 
Clean architecture with asp.net core
Clean architecture with asp.net coreClean architecture with asp.net core
Clean architecture with asp.net coreSam Nasr, MCSA, MVP
 
Clean architecture with ddd layering in php
Clean architecture with ddd layering in phpClean architecture with ddd layering in php
Clean architecture with ddd layering in phpLeonardo Proietti
 
GraphQL IN Golang
GraphQL IN GolangGraphQL IN Golang
GraphQL IN GolangBo-Yi Wu
 
A Practical Guide to Domain Driven Design: Presentation Slides
A Practical Guide to Domain Driven Design: Presentation SlidesA Practical Guide to Domain Driven Design: Presentation Slides
A Practical Guide to Domain Driven Design: Presentation Slidesthinkddd
 
Domain Driven Design (Ultra) Distilled
Domain Driven Design (Ultra) DistilledDomain Driven Design (Ultra) Distilled
Domain Driven Design (Ultra) DistilledNicola Costantino
 

Mais procurados (20)

Clean Architecture
Clean ArchitectureClean Architecture
Clean Architecture
 
Domain Driven Design
Domain Driven DesignDomain Driven Design
Domain Driven Design
 
Domain driven design
Domain driven designDomain driven design
Domain driven design
 
Implementing DDD Concepts in PHP
Implementing DDD Concepts in PHPImplementing DDD Concepts in PHP
Implementing DDD Concepts in PHP
 
Clean Architecture
Clean ArchitectureClean Architecture
Clean Architecture
 
Clean Architecture Essentials - Stockholm Software Craftsmanship
Clean Architecture Essentials - Stockholm Software CraftsmanshipClean Architecture Essentials - Stockholm Software Craftsmanship
Clean Architecture Essentials - Stockholm Software Craftsmanship
 
Domain Driven Design(DDD) Presentation
Domain Driven Design(DDD) PresentationDomain Driven Design(DDD) Presentation
Domain Driven Design(DDD) Presentation
 
Introduction to DDD
Introduction to DDDIntroduction to DDD
Introduction to DDD
 
Introducing Domain Driven Design - codemash
Introducing Domain Driven Design - codemashIntroducing Domain Driven Design - codemash
Introducing Domain Driven Design - codemash
 
Deep dive into swift UI
Deep dive into swift UIDeep dive into swift UI
Deep dive into swift UI
 
Hexagonal architecture - message-oriented software design
Hexagonal architecture  - message-oriented software designHexagonal architecture  - message-oriented software design
Hexagonal architecture - message-oriented software design
 
Mucon 2019: OOps I DDD it again and again
Mucon 2019: OOps I DDD it again and againMucon 2019: OOps I DDD it again and again
Mucon 2019: OOps I DDD it again and again
 
Brownfield Domain Driven Design
Brownfield Domain Driven DesignBrownfield Domain Driven Design
Brownfield Domain Driven Design
 
AxonIQCon22 - Beyond DDD 101 - Zambrovski-Galinski.pptx
AxonIQCon22 - Beyond DDD 101 - Zambrovski-Galinski.pptxAxonIQCon22 - Beyond DDD 101 - Zambrovski-Galinski.pptx
AxonIQCon22 - Beyond DDD 101 - Zambrovski-Galinski.pptx
 
Clean architecture with asp.net core
Clean architecture with asp.net coreClean architecture with asp.net core
Clean architecture with asp.net core
 
Clean architecture with ddd layering in php
Clean architecture with ddd layering in phpClean architecture with ddd layering in php
Clean architecture with ddd layering in php
 
GraphQL IN Golang
GraphQL IN GolangGraphQL IN Golang
GraphQL IN Golang
 
A Practical Guide to Domain Driven Design: Presentation Slides
A Practical Guide to Domain Driven Design: Presentation SlidesA Practical Guide to Domain Driven Design: Presentation Slides
A Practical Guide to Domain Driven Design: Presentation Slides
 
Domain Driven Design
Domain Driven DesignDomain Driven Design
Domain Driven Design
 
Domain Driven Design (Ultra) Distilled
Domain Driven Design (Ultra) DistilledDomain Driven Design (Ultra) Distilled
Domain Driven Design (Ultra) Distilled
 

Semelhante a Acercándonos a la Programación Funcional a través de la Arquitectura Hexagonal - ScMad+ScalaMad

Django Introduction & Tutorial
Django Introduction & TutorialDjango Introduction & Tutorial
Django Introduction & Tutorial之宇 趙
 
Apigility-powered API's on IBM i
Apigility-powered API's on IBM iApigility-powered API's on IBM i
Apigility-powered API's on IBM ichukShirley
 
Introduction To Code Igniter
Introduction To Code IgniterIntroduction To Code Igniter
Introduction To Code IgniterAmzad Hossain
 
Having fun with code igniter
Having fun with code igniterHaving fun with code igniter
Having fun with code igniterAhmad Arif
 
Building a REST API Microservice for the DevNet API Scavenger Hunt
Building a REST API Microservice for the DevNet API Scavenger HuntBuilding a REST API Microservice for the DevNet API Scavenger Hunt
Building a REST API Microservice for the DevNet API Scavenger HuntAshley Roach
 
From Backbone to Ember and Back(bone) Again
From Backbone to Ember and Back(bone) AgainFrom Backbone to Ember and Back(bone) Again
From Backbone to Ember and Back(bone) Againjonknapp
 
“ASP.NET Core. Features and architecture”
“ASP.NET Core. Features and architecture” “ASP.NET Core. Features and architecture”
“ASP.NET Core. Features and architecture” HYS Enterprise
 
Getting Started with API Management – Why It's Needed On-prem and in the Cloud
Getting Started with API Management – Why It's Needed On-prem and in the CloudGetting Started with API Management – Why It's Needed On-prem and in the Cloud
Getting Started with API Management – Why It's Needed On-prem and in the CloudRevelation Technologies
 
Serverless Single Page Apps with React and Redux at ItCamp 2017
Serverless Single Page Apps with React and Redux at ItCamp 2017Serverless Single Page Apps with React and Redux at ItCamp 2017
Serverless Single Page Apps with React and Redux at ItCamp 2017Melania Andrisan (Danciu)
 
Let's build Developer Portal with Backstage
Let's build Developer Portal with BackstageLet's build Developer Portal with Backstage
Let's build Developer Portal with BackstageOpsta
 
From framework coupled code to #microservices through #DDD /by @codelytv
From framework coupled code to #microservices through #DDD /by @codelytvFrom framework coupled code to #microservices through #DDD /by @codelytv
From framework coupled code to #microservices through #DDD /by @codelytvCodelyTV
 
When to use Serverless? When to use Kubernetes?
When to use Serverless? When to use Kubernetes?When to use Serverless? When to use Kubernetes?
When to use Serverless? When to use Kubernetes?Niklas Heidloff
 
Deploying Next Gen Systems with Zero Downtime
Deploying Next Gen Systems with Zero DowntimeDeploying Next Gen Systems with Zero Downtime
Deploying Next Gen Systems with Zero DowntimeTwilio Inc
 
Apic dc api deep dive
Apic dc api deep dive Apic dc api deep dive
Apic dc api deep dive Cisco DevNet
 
WordCamp LA 2014- Writing Code that Scales
WordCamp LA 2014-  Writing Code that ScalesWordCamp LA 2014-  Writing Code that Scales
WordCamp LA 2014- Writing Code that ScalesSpectrOMTech.com
 
Hanselman lipton asp_connections_ams304_mvc
Hanselman lipton asp_connections_ams304_mvcHanselman lipton asp_connections_ams304_mvc
Hanselman lipton asp_connections_ams304_mvcdenemedeniz
 
Developing ASP.NET Applications Using the Model View Controller Pattern
Developing ASP.NET Applications Using the Model View Controller PatternDeveloping ASP.NET Applications Using the Model View Controller Pattern
Developing ASP.NET Applications Using the Model View Controller Patterngoodfriday
 
@avanttic_meetup Oracle Technology MAD_BCN: Oracle Cloud API Platform evoluc...
@avanttic_meetup Oracle Technology MAD_BCN:  Oracle Cloud API Platform evoluc...@avanttic_meetup Oracle Technology MAD_BCN:  Oracle Cloud API Platform evoluc...
@avanttic_meetup Oracle Technology MAD_BCN: Oracle Cloud API Platform evoluc...avanttic Consultoría Tecnológica
 
ZZ BC#7 asp.net mvc practice and guideline by NineMvp
ZZ BC#7 asp.net mvc practice and guideline by NineMvpZZ BC#7 asp.net mvc practice and guideline by NineMvp
ZZ BC#7 asp.net mvc practice and guideline by NineMvpChalermpon Areepong
 

Semelhante a Acercándonos a la Programación Funcional a través de la Arquitectura Hexagonal - ScMad+ScalaMad (20)

Django Introduction & Tutorial
Django Introduction & TutorialDjango Introduction & Tutorial
Django Introduction & Tutorial
 
Apigility-powered API's on IBM i
Apigility-powered API's on IBM iApigility-powered API's on IBM i
Apigility-powered API's on IBM i
 
AWValuePitch, 7_12
AWValuePitch, 7_12AWValuePitch, 7_12
AWValuePitch, 7_12
 
Introduction To Code Igniter
Introduction To Code IgniterIntroduction To Code Igniter
Introduction To Code Igniter
 
Having fun with code igniter
Having fun with code igniterHaving fun with code igniter
Having fun with code igniter
 
Building a REST API Microservice for the DevNet API Scavenger Hunt
Building a REST API Microservice for the DevNet API Scavenger HuntBuilding a REST API Microservice for the DevNet API Scavenger Hunt
Building a REST API Microservice for the DevNet API Scavenger Hunt
 
From Backbone to Ember and Back(bone) Again
From Backbone to Ember and Back(bone) AgainFrom Backbone to Ember and Back(bone) Again
From Backbone to Ember and Back(bone) Again
 
“ASP.NET Core. Features and architecture”
“ASP.NET Core. Features and architecture” “ASP.NET Core. Features and architecture”
“ASP.NET Core. Features and architecture”
 
Getting Started with API Management – Why It's Needed On-prem and in the Cloud
Getting Started with API Management – Why It's Needed On-prem and in the CloudGetting Started with API Management – Why It's Needed On-prem and in the Cloud
Getting Started with API Management – Why It's Needed On-prem and in the Cloud
 
Serverless Single Page Apps with React and Redux at ItCamp 2017
Serverless Single Page Apps with React and Redux at ItCamp 2017Serverless Single Page Apps with React and Redux at ItCamp 2017
Serverless Single Page Apps with React and Redux at ItCamp 2017
 
Let's build Developer Portal with Backstage
Let's build Developer Portal with BackstageLet's build Developer Portal with Backstage
Let's build Developer Portal with Backstage
 
From framework coupled code to #microservices through #DDD /by @codelytv
From framework coupled code to #microservices through #DDD /by @codelytvFrom framework coupled code to #microservices through #DDD /by @codelytv
From framework coupled code to #microservices through #DDD /by @codelytv
 
When to use Serverless? When to use Kubernetes?
When to use Serverless? When to use Kubernetes?When to use Serverless? When to use Kubernetes?
When to use Serverless? When to use Kubernetes?
 
Deploying Next Gen Systems with Zero Downtime
Deploying Next Gen Systems with Zero DowntimeDeploying Next Gen Systems with Zero Downtime
Deploying Next Gen Systems with Zero Downtime
 
Apic dc api deep dive
Apic dc api deep dive Apic dc api deep dive
Apic dc api deep dive
 
WordCamp LA 2014- Writing Code that Scales
WordCamp LA 2014-  Writing Code that ScalesWordCamp LA 2014-  Writing Code that Scales
WordCamp LA 2014- Writing Code that Scales
 
Hanselman lipton asp_connections_ams304_mvc
Hanselman lipton asp_connections_ams304_mvcHanselman lipton asp_connections_ams304_mvc
Hanselman lipton asp_connections_ams304_mvc
 
Developing ASP.NET Applications Using the Model View Controller Pattern
Developing ASP.NET Applications Using the Model View Controller PatternDeveloping ASP.NET Applications Using the Model View Controller Pattern
Developing ASP.NET Applications Using the Model View Controller Pattern
 
@avanttic_meetup Oracle Technology MAD_BCN: Oracle Cloud API Platform evoluc...
@avanttic_meetup Oracle Technology MAD_BCN:  Oracle Cloud API Platform evoluc...@avanttic_meetup Oracle Technology MAD_BCN:  Oracle Cloud API Platform evoluc...
@avanttic_meetup Oracle Technology MAD_BCN: Oracle Cloud API Platform evoluc...
 
ZZ BC#7 asp.net mvc practice and guideline by NineMvp
ZZ BC#7 asp.net mvc practice and guideline by NineMvpZZ BC#7 asp.net mvc practice and guideline by NineMvp
ZZ BC#7 asp.net mvc practice and guideline by NineMvp
 

Último

Artyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxArtyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxAnnaArtyushina1
 
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024VictoriaMetrics
 
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...masabamasaba
 
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benonimasabamasaba
 
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Bert Jan Schrijver
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...panagenda
 
WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...masabamasaba
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastPapp Krisztián
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park masabamasaba
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is insideshinachiaurasa2
 
%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in sowetomasabamasaba
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park masabamasaba
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnAmarnathKambale
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Steffen Staab
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyviewmasabamasaba
 

Último (20)

Artyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxArtyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptx
 
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
 
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
 
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
 
Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
 
WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go Platformless
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the past
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
 
%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
 

Acercándonos a la Programación Funcional a través de la Arquitectura Hexagonal - ScMad+ScalaMad

  • 1. Acercándonos a la Programación Funcional 🦄 a través de la Arquitectura Hexagonal 🎯 Habla Computing + CodelyTV
  • 2. Madrid, 21 de Noviembre de 2018
  • 3. Functional Programming intro based on Hexagonal Architecture Who we are @JavierCane@juanshac
  • 4. Session goal 🤔
 Rethinking time Functional Programming intro based on Hexagonal Architecture Reviewing different technics to solve the same problems with more modularity
  • 5. 🗺Design principles %Implementation details 🦄 Functional Architectures 🎯 Hexagonal Architecture Functional Programming intro based on Hexagonal Architecture 🦄 Functional Architectures 🎯 Hexagonal Architecture 🏗 APIs ⚡ Errors 🏗 APIs ⚡ Errors
  • 6. 🗺Design principles %Implementation details 🦄 Functional Architectures 🎯 Hexagonal Architecture Functional Programming intro based on Hexagonal Architecture 🦄 Functional Architectures 🎯 Hexagonal Architecture 🏗 APIs ⚡ Errors 🏗 APIs ⚡ Errors
  • 7. 🗺 Design principles > 🎯 Hexagonal Architecture 🛀 Clean Architectures
  • 8. 🤯 Ports & adapters 🚫 🗺 Design principles > 🎯 Hexagonal Architecture
  • 9. Layers & dependency rule Infrastructure Application Domain 🗺 Design principles > 🎯 Hexagonal Architecture
  • 10. Layered Architecture Presentation Domain Database 🗺 Design principles > 🎯 Hexagonal Architecture
  • 12.
  • 13. CONTROLLER REPOS MODELS SERVICES APPLICATION SERVICE D A I Request flow IMPLEMENTATION Acceptance test Unit test Integration test 🗺 Design principles > 🎯 Hexagonal Architecture
  • 14. Acceptance test Unit test Integration test CONTROLLER REPOS MODELS SERVICES APPLICATION SERVICE D A I Request flow IMPLEMENTATION 🗺 Design principles > 🎯 Hexagonal Architecture Port Adapter
  • 15. VideoPostController VideoRepository VideoVideoCreator D A I DoobieMySqlVideoRepo Acceptance test Unit test Integration test Request flow example 🗺 Design principles > 🎯 Hexagonal Architecture
  • 16. VideoPostController VideoRepository VideoVideoCreator D A I DoobieMySqlVideoRepo Acceptance test Unit test Integration test Port Adapter 🗺 Design principles > 🎯 Hexagonal Architecture Request flow example
  • 17.
  • 18. Domain Events, CQRS, and Event Sourcing as an optional further step
  • 19. 🗺Design principles %Implementation details 🦄 Functional Architectures 🎯 Hexagonal Architecture Functional Programming intro based on Hexagonal Architecture 🦄 Functional Architectures 🎯 Hexagonal Architecture 🏗 APIs ⚡ Errors 🏗 APIs ⚡ Errors
  • 20. 🗺Design principles %Implementation details 🦄 Functional Architectures 🎯 Hexagonal Architecture Functional Programming intro based on Hexagonal Architecture 🦄 Functional Architectures 🎯 Hexagonal Architecture 🏗 APIs ⚡ Errors 🏗 APIs ⚡ Errors
  • 21. What is a functional architecture? DSL1 DSL2 DSLN … 🗺 Design principles > 🦄 Functional Architectures A FUNCTIONAL LANGUAGE IS A DOMAIN- SPECIFIC LANGUAGE FOR DEFINING DOMAIN-SPECIFIC LANGUAGES
  • 22. The DSLs of your application • Infrastructure DSLs • HTTP • Databases • Messaging • Application-specific DSLs • Use cases • Repos • … HTTP SERVICE DSL SQL REPO DSL 🗺 Design principles > 🦄 Functional Architectures
  • 23. Are hexagonal and functional architectures like apples and oranges? HTTP SERVICE DSL SQL REPO DSL CONTROLLER SERVICE IMPL. ORM DB ADAPTER/INTERP. PORT/DSL 🗺 Design principles > 🦄 Functional Architectures
  • 24. There are no exceptions • Everything is either a port or an adapter • Every adapter implements a given port • Adapters are implemented on top of other ports VideoPostController VideoRepositoryVideoRepoC DoobieMySqlVideoRepo VideoCreator HTTP 🗺 Design principles > 🦄 Functional Architectures
  • 26. 🗺 Design principles > 🦄 Functional Architectures … VideoPostController VideoRepoC VideoCreator HTTPVideoPostClient VideoCreator… …… Why to declare the Application Service API?
  • 27. 🤔 Rethinking time 🗺 Design principles
  • 28. 🗺 Design principles > 🤔 Rethinking time Final goals • Decouple from infrastructure • Testing easiness (test doubles when testing out use cases) • Change tolerance • ¡Same goals!
  • 29. 🗺 Design principles > 🤔 Rethinking time Differences: More decoupling and indirection levels • Controllers: • Consistency (APIs for all DSLs) vs. Specific needs • Conclusion: We need industry standards. PHP-FIG as example to follow • Application Services: • Consistency (APIs for all DSLs) vs. Specific needs • Reuse use case APIs in clients (e.g. user clients)
  • 30. 🗺Design principles %Implementation details 🦄 Functional Architectures 🎯 Hexagonal Architecture Functional Programming intro based on Hexagonal Architecture 🦄 Functional Architectures 🎯 Hexagonal Architecture 🏗 APIs ⚡ Errors 🏗 APIs ⚡ Errors
  • 31. 🗺Design principles %Implementation details 🦄 Functional Architectures 🎯 Hexagonal Architecture Functional Programming intro based on Hexagonal Architecture 🦄 Functional Architectures 🎯 Hexagonal Architecture 🏗 APIs ⚡ Errors 🏗 APIs ⚡ Errors
  • 32. Remembering the Architecture layers VideoPostController VideoRepositoryVideoCreator DoctrineMySqlVideoRepo App Dom Infra
  • 34. final class VideoPostController extends ApiController { private $creator; public function __construct(VideoCreator $creator) { parent::__construct(); $this->creator = $creator; } public function __invoke(Request $request) { VideoPostController - HTTP API Controller VideoPostController VideoRepositoryVideoCreator DoctrineMySqlVideoRepo
  • 35. final class VideoPostController extends ApiController { private $creator; public function __construct(VideoCreator $creator) { parent::__construct(); $this->creator = $creator; } public function __invoke(Request $request) { VideoPostController - HTTP API Controller VideoPostController VideoRepositoryVideoCreator DoctrineMySqlVideoRepo
  • 36. final class VideoPostController extends ApiController { private $creator; public function __construct(VideoCreator $creator) { parent::__construct(); $this->creator = $creator; } public function __invoke(Request $request) { $id = new VideoId($request->get('id')); $type = new VideoType($request->get('type')); $title = new VideoTitle($request->get('title')); $url = new VideoUrl($request->get('url')); $courseId = new CourseId($request->get('course_id')); $this->creator->create( $id, $type, $title, $url, $courseId ); return new ApiHttpCreatedResponse(); VideoPostController - HTTP API Controller VideoPostController VideoRepositoryVideoCreator DoctrineMySqlVideoRepo
  • 37. final class VideoPostController extends ApiController { private $creator; public function __construct(VideoCreator $creator) { parent::__construct(); $this->creator = $creator; } public function __invoke(Request $request) { $id = new VideoId($request->get('id')); $type = new VideoType($request->get('type')); $title = new VideoTitle($request->get('title')); $url = new VideoUrl($request->get('url')); $courseId = new CourseId($request->get('course_id')); $this->creator->create( $id, $type, $title, $url, $courseId ); return new ApiHttpCreatedResponse(); VideoPostController - HTTP API Controller VideoPostController VideoRepositoryVideoCreator DoctrineMySqlVideoRepo
  • 38. final class VideoCreator { private $repository; private $publisher; public function __construct( VideoRepository $repository, DomainEventPublisher $publisher ) { $this->repository = $repository; $this->publisher = $publisher; } public function create( VideoCreator - Application Service/Use case VideoPostController VideoRepositoryVideoCreator DoobieMySqlVideoRepoDoctrineMySqlVideoRepo
  • 39. final class VideoCreator { private $repository; private $publisher; public function __construct( VideoRepository $repository, DomainEventPublisher $publisher ) { $this->repository = $repository; $this->publisher = $publisher; } public function create( VideoCreator - Application Service/Use case VideoPostController VideoRepositoryVideoCreator DoctrineMySqlVideoRepo
  • 40. private $repository; private $publisher; public function __construct( VideoRepository $repository, DomainEventPublisher $publisher ) { $this->repository = $repository; $this->publisher = $publisher; } public function create( VideoId $id, VideoType $type, VideoTitle $title, VideoUrl $url, CourseId $courseId ) { $video = Video::create($id, $type, $title, $url, $courseId); $this->repository->save($video); $this->publisher->publish(...$video->pullDomainEvents()); } } VideoCreator - Application Service/Use case VideoPostController VideoRepositoryVideoCreator DoctrineMySqlVideoRepo
  • 41. private $repository; private $publisher; public function __construct( VideoRepository $repository, DomainEventPublisher $publisher ) { $this->repository = $repository; $this->publisher = $publisher; } public function create( VideoId $id, VideoType $type, VideoTitle $title, VideoUrl $url, CourseId $courseId ) { $video = Video::create($id, $type, $title, $url, $courseId); $this->repository->save($video); $this->publisher->publish(...$video->pullDomainEvents()); } } VideoCreator - Application Service/Use case VideoPostController VideoRepositoryVideoCreator DoctrineMySqlVideoRepo
  • 42. public static function create( VideoId $id, VideoType $type, VideoTitle $title, VideoUrl $url, CourseId $courseId ): Video { $video = new self($id, $type, $title, $url, $courseId); $video->record( new VideoCreatedDomainEvent( $id->value(), [ 'type' => $type->value(), 'title' => $title->value(), 'url' => $url->value(), 'courseId' => $courseId->value(), ] ) ); return $video; Video#create - AggregateRoot
  • 43. public static function create( VideoId $id, VideoType $type, VideoTitle $title, VideoUrl $url, CourseId $courseId ): Video { $video = new self($id, $type, $title, $url, $courseId); $video->record( new VideoCreatedDomainEvent( $id->value(), [ 'type' => $type->value(), 'title' => $title->value(), 'url' => $url->value(), 'courseId' => $courseId->value(), ] ) ); return $video; Video#create - AggregateRoot
  • 44. private $repository; private $publisher; public function __construct( VideoRepository $repository, DomainEventPublisher $publisher ) { $this->repository = $repository; $this->publisher = $publisher; } public function create( VideoId $id, VideoType $type, VideoTitle $title, VideoUrl $url, CourseId $courseId ) { $video = Video::create($id, $type, $title, $url, $courseId); $this->repository->save($video); $this->publisher->publish(...$video->pullDomainEvents()); } } VideoCreator - Application Service/Use case VideoPostController VideoRepositoryVideoCreator DoctrineMySqlVideoRepo
  • 45. interface VideoRepository { public function save(Video $video): void; public function search(VideoId $id): ?Video; public function searchByCriteria(Criteria $criteria): Videos; } VideoRepository - Domain contract/Port VideoPostController VideoRepositoryVideoCreator DoctrineMySqlVideoRepo
  • 46. DoobieMySqlVideoRepository - Infrastructure implementation/Adapter VideoPostController VideoRepositoryVideoCreator final class DoctrineMySqlVideoRepository implements VideoRepository { private $entityManager; public function __construct(EntityManager $entityManager) { $this->entityManager = $entityManager; } public function save(Video $video): void { $this->entityManager()->persist($entity); DoctrineMySqlVideoRepo
  • 47. final class DoctrineMySqlVideoRepository implements VideoRepository { private $entityManager; public function __construct(EntityManager $entityManager) { $this->entityManager = $entityManager; } public function save(Video $video): void { $this->entityManager()->persist($entity); $this->entityManager()->flush($entity); } // ... } DoobieMySqlVideoRepository - Infrastructure implementation/Adapter VideoPostController VideoRepositoryVideoCreator DoctrineMySqlVideoRepo
  • 48. final class DoctrineMySqlVideoRepository implements VideoRepository { private $entityManager; public function __construct(EntityManager $entityManager) { $this->entityManager = $entityManager; } public function save(Video $video): void { $this->entityManager()->persist($entity); $this->entityManager()->flush($entity); } // ... } DoobieMySqlVideoRepository - Infrastructure implementation/Adapter VideoPostController VideoRepositoryVideoCreator DoctrineMySqlVideoRepo
  • 50. final class VideoPostController(creator: VideoCreator) { def post(id: String, title: String): StandardRoute = { creator.create(VideoId(id), VideoTitle(title)) complete(HttpResponse(Created)) } } VideoPostController - HTTP API Controller VideoPostController VideoRepositoryVideoCreator DoobieMySqlVideoRepo
  • 51. final class VideoPostController(creator: VideoCreator) { def post(id: String, title: String): StandardRoute = { creator.create(VideoId(id), VideoTitle(title)) complete(HttpResponse(Created)) } } VideoPostController - HTTP API Controller VideoPostController VideoRepositoryVideoCreator DoobieMySqlVideoRepo
  • 52. final class VideoPostController(creator: VideoCreator) { def post(id: String, title: String): StandardRoute = { creator.create(VideoId(id), VideoTitle(title)) complete(HttpResponse(Created)) } } VideoPostController - HTTP API Controller VideoPostController VideoRepositoryVideoCreator DoobieMySqlVideoRepo
  • 53. object VideoQuality { val maxQuality = 50 val minQuality = 0 } final case class VideoQuality(value: Int) { require(value <= maxQuality, s"Video quality value greater than $maxQuality") require(value >= minQuality, s"Video quality value less than $minQuality") def increase(): VideoQuality = { if (value >= maxQuality) this else copy(value + 1) } } OOP: Data+Behaviour 👤 Domain models > 🎯 OOP > VideoQuality Value Object
  • 54. object VideoQuality { val maxQuality = 50 val minQuality = 0 } final case class VideoQuality(value: Int) { require(value <= maxQuality, s"Video quality value greater than $maxQuality") require(value >= minQuality, s"Video quality value less than $minQuality") def increase(): VideoQuality = { if (value >= maxQuality) this else copy(value + 1) } } Behaviour Simpler API + domain semantics + immutability 👤 Domain models > 🎯 OOP > VideoQuality Value Object
  • 55. case class Video( id: VideoId, title: VideoTitle, quality: VideoQuality ) { def increaseQuality(): Video = copy(quality = quality.increase()) } VideoQualityIncreaser Video increaseQuality() Behaviour Rich domain model - Tell don’t ask 👤 Domain models > 🎯 OOP > Video Entity
  • 56. final class VideoPostController(creator: VideoCreator) { def post(id: String, title: String): StandardRoute = { creator.create(VideoId(id), VideoTitle(title)) complete(HttpResponse(Created)) } } VideoPostController - HTTP API Controller VideoPostController VideoRepositoryVideoCreator DoobieMySqlVideoRepo
  • 57. final class VideoPostController(creator: VideoCreator) { def post(id: String, title: String): StandardRoute = { creator.create(VideoId(id), VideoTitle(title)) complete(HttpResponse(Created)) } } VideoPostController - HTTP API Controller VideoPostController VideoRepositoryVideoCreator DoobieMySqlVideoRepo
  • 58. final class VideoCreator( repository: VideoRepository, publisher: MessagePublisher ) { def create(id: VideoId, title: VideoTitle): Unit = { val video = Video(id, title) repository.save(video) publisher.publish(VideoCreated(video)) } } VideoCreator - Application Service/Use case VideoPostController VideoRepositoryVideoCreator DoobieMySqlVideoRepo
  • 59. final class VideoCreator( repository: VideoRepository, publisher: MessagePublisher ) { def create(id: VideoId, title: VideoTitle): Unit = { val video = Video(id, title) repository.save(video) publisher.publish(VideoCreated(video)) } } VideoCreator - Application Service/Use case VideoPostController VideoRepositoryVideoCreator DoobieMySqlVideoRepo
  • 60. final class VideoCreator( repository: VideoRepository, publisher: MessagePublisher ) { def create(id: VideoId, title: VideoTitle): Unit = { val video = Video(id, title) repository.save(video) publisher.publish(VideoCreated(video)) } } VideoCreator - Application Service/Use case VideoPostController VideoRepositoryVideoCreator DoobieMySqlVideoRepo
  • 61. trait VideoRepository { def all(): Future[Seq[Video]] def save(video: Video): Future[Unit] } VideoRepository - Domain contract/Port VideoPostController VideoRepositoryVideoCreator DoobieMySqlVideoRepo
  • 62. DoobieMySqlVideoRepository - Infrastructure implementation/Adapter VideoPostController VideoRepositoryVideoCreator DoobieMySqlVideoRepo final class DoobieMySqlVideoRepository(db: DoobieDbConnection) (implicit ec: ExecutionContext) extends VideoRepository { override def all(): Future[Seq[Video]] = db.read(sql”SELECT…”.query[Video].to[Seq]) override def save(video: Video): Future[Unit] = sql"INSERT INTO…”.update.run .transact(db.transactor) .unsafeToFuture().map(_ => ()) }
  • 63. 🗺Design principles %Implementation details 🦄 Functional Architectures 🎯 Hexagonal Architecture Functional Programming intro based on Hexagonal Architecture 🦄 Functional Architectures 🎯 Hexagonal Architecture 🏗 APIs ⚡ Errors 🏗 APIs ⚡ Errors
  • 64. 🗺Design principles %Implementation details 🦄 Functional Architectures 🎯 Hexagonal Architecture Functional Programming intro based on Hexagonal Architecture 🦄 Functional Architectures 🎯 Hexagonal Architecture 🏗 APIs ⚡ Errors 🏗 APIs ⚡ Errors
  • 65. object VideoQuality { val maxQuality = 50 val minQuality = 0 } final case class VideoQuality(value: Int) { require(value <= maxQuality, s"Video quality value greater than $maxQuality") require(value >= minQuality, s"Video quality value less than $minQuality") def increase(): VideoQuality = { if (value >= maxQuality) this else copy(value + 1) } } VO: Raise exceptions for invalid states ⚡Error Handling > 🎯 OOP > Raise exceptions
  • 66. final class VideoFinder(repository: VideoRepository) (implicit ec: ExecutionContext) { def find(id: VideoId): Future[Video] = repository.search(id).map { videoOption => if (videoOption.isEmpty) throw VideoNotFound(id) else videoOption.get } } Imperativeness ⚡Error Handling > 🎯 OOP > Raise exceptions trait VideoRepository { def search(id: VideoId): Future[Option[Video]] }
  • 67. final class VideoGetController extends ApiController { protected function exceptions(): array { return [ VideoNotFound::class => Response::HTTP_NOT_FOUND, ]; } // … } HTTP Mapping ⚡Error Handling > 🎯 OOP > Handle exceptions
  • 68. 🗺Design principles %Implementation details 🦄 Functional Architectures 🎯 Hexagonal Architecture Functional Programming intro based on Hexagonal Architecture 🦄 Functional Architectures 🎯 Hexagonal Architecture 🏗 APIs ⚡ Errors 🏗 APIs ⚡ Errors
  • 69. 🗺Design principles %Implementation details 🦄 Functional Architectures 🎯 Hexagonal Architecture Functional Programming intro based on Hexagonal Architecture 🦄 Functional Architectures 🎯 Hexagonal Architecture 🏗 APIs ⚡ Errors 🏗 APIs ⚡ Errors
  • 70. 🗺 Design principles > 🦄 Functional Architectures VideoController VideoFinderRepo VideoFinder HTTPVideoClient VideoFinder… … DoobieRepo… VideoRepo API
  • 71. trait VideoRepository { def all(): Future[Seq[Video]] def save(video: Video): Future[Unit] } Domain contract/PortVideoRepository ? trait VideoRepository { def all(): cats.IO[Seq[Video]] def save(video: Video): cats.IO[Unit] } trait VideoRepository { def all(): Seq[Video] def save(video: Video): Unit }
  • 72. trait VideoRepository { def all(): Seq[Video] def save(video: Video): Unit } trait VideoRepository { def all(): Future[Seq[Video]] def save(video: Video): Future[Unit] } trait VideoRepository { def all(): cats.IO[Seq[Video]] def save(video: Video): cats.IO[Unit] } Domain contract/Port infrastructure leaks VideoRepository ?
  • 73. trait VideoRepository[P[_]] { def all(): P[Seq[Video]] def save(video: Video): P[Unit] } TYPE (CONSTRUCTOR) CLASSES VideoRepository ? Domain contract/Port
  • 74. 🗺 Design principles > 🦄 Functional Architectures VideoController VideoFinderRepo VideoFinder HTTPVideoClient VideoFinder… … DoobieRepo… VideoRepo INSTANCE
  • 75. Infrastructure implementation/Adapter import scala.concurent.Future final class DoobieMySqlVideoRepoFuture( db: DoobieDbConnection[Future])(implicit ec: ExecutionContext) extends VideoRepository[Future] { override def all(): Future[Seq[Video]] = db.read(sql"SELECT ...".query[Video].to[Seq]) override def save(video: Video): Future[Unit] = sql"INSERT INTO ... ".update.run .transact(db.transactor) .map(_ => ()) } VideoRepository DoobieVideoRepoFuture INSTANTIATION
  • 76. Infrastructure implementation/Adapter import scala.concurent.Future final class DoobieMySqlVideoRepoFuture( db: DoobieDbConnection[Future])(implicit ec: ExecutionContext) extends VideoRepository[Future] { override def all(): Future[Seq[Video]] = db.read(sql"SELECT ...".query[Video].to[Seq]) override def save(video: Video): Future[Unit] = sql"INSERT INTO ... ".update.run .transact(db.transactor) .map(_ => ()) } VideoRepository DoobieVideoRepoFuture INSTANTIATION
  • 77. Infrastructure implementation/AdapterVideoRepository DoobieVideoRepoIO import cats.effect.IO final class DoobieMySqlVideoRepoIO( db: DoobieDbConnection[IO]) extends VideoRepository[IO] { override def all(): IO[Seq[Video]] = db.read(sql"SELECT ...".query[Video].to[Seq]) override def save(video: Video): IO[Unit] = sql"INSERT INTO ... ".update.run .transact(db.transactor) .map(_ => ()) } INSTANTIATION
  • 78. Infrastructure implementation/AdapterVideoRepository DoobieVideoRepoIO import cats.effect.IO final class DoobieMySqlVideoRepoIO( db: DoobieDbConnection[IO]) extends VideoRepository[IO] { override def all(): IO[Seq[Video]] = db.read(sql"SELECT ...".query[Video].to[Seq]) override def save(video: Video): IO[Unit] = sql"INSERT INTO ... ".update.run .transact(db.transactor) .map(_ => ()) } INSTANTIATION
  • 79. Infrastructure implementation/AdapterVideoRepository DoobieVideoRepo (GENERIC) INSTANTIATION final case class DoobieMySqlVideoRepo[P[_]: Monad]()( db: DoobieDbConnection[P]) extends VideoRepository[P] { override def all(): P[Seq[Video]] = db.read(sql"SELECT …”.query[Video].to[Seq]) override def save(video: Video): P[Unit] = sql"INSERT …”.update.run .transact(db.transactor) .map(_ => ()) }
  • 80. Infrastructure implementation/AdapterVideoRepository DoobieVideoRepo (GENERIC) INSTANTIATION final case class DoobieMySqlVideoRepo[P[_]: Monad]()( db: DoobieDbConnection[P]) extends VideoRepository[P] { override def all(): P[Seq[Video]] = db.read(sql"SELECT …”.query[Video].to[Seq]) override def save(video: Video): P[Unit] = sql"INSERT …”.update.run .transact(db.transactor) .map(_ => ()) }
  • 81. 🗺Design principles %Implementation details 🦄 Functional Architectures 🎯 Hexagonal Architecture Functional Programming intro based on Hexagonal Architecture 🦄 Functional Architectures 🎯 Hexagonal Architecture 🏗 APIs ⚡ Errors 🏗 APIs ⚡ Errors
  • 82. 🗺Design principles %Implementation details 🦄 Functional Architectures 🎯 Hexagonal Architecture Functional Programming intro based on Hexagonal Architecture 🦄 Functional Architectures 🎯 Hexagonal Architecture 🏗 APIs ⚡ Errors 🏗 APIs ⚡ Errors
  • 83. 🗺 Design principles > 🦄 Functional Architectures VideoController VideoFinderRepo VideoFinder HTTPVideoClient VideoFinder… … DoobieRepo… VideoRepo API
  • 84. ⚡Error Handling > 🦄 FP > Raise errors trait VideoFinder[P[_]]{ def find(id: VideoId)(implicit E: MonadError[P, VideoFinderError]): P[Video] } A functional signature doesn’t hide anything! trait MonadError[F[_], E] { def raiseError[A](e: E): F[A] def handleErrorWith[A](fa: F[A])(f: E => F[A]): F[A] }
  • 85. 🗺 Design principles > 🦄 Functional Architectures VideoController VideoFinderRepo VideoFinder HTTPVideoClient VideoFinder… … DoobieRepo… VideoRepo INSTANCE / LOGIC
  • 86. Imperativeness ⚡Error Handling > 🦄 FP > Raise errors Declarativeness + final case class VideoFinderRepo[P[_]]()( implicit repository: VideoRepository[P] ) extends VideoFinder[P] { def find(id: VideoId)(implicit E: MonadError[P, VideoFinderError]): P[Video] = repository.search(id) flatMap { maybeVideo => maybeVideo.fold(E.raiseError[Video](VideoNotFound(id))){ video => video.pure[P] } } }
  • 87. ⚡Error Handling > 🦄 FP > Raise errors final case class VideoFinderRepo[P[_]: Monad]()( implicit repository: VideoRepository[P] ) extends VideoFinder[P] { def find(id: VideoId)(implicit E: MonadError[P, VideoFinderError]): P[Video] = repository.search(id) flatMap { maybeVideo => maybeVideo.fold(E.raiseError[Video](VideoNotFound(id))){ video => video.pure[P] } } } Imperativeness
  • 88. ⚡Error Handling > 🦄 FP > Raise errors final case class VideoFinderRepo[P[_]: Monad]()( implicit repository: VideoRepository[P] ) extends VideoFinder[P] { def find(id: VideoId)(implicit E: MonadError[P, VideoFinderError]): P[Video] = val maybeVideo = repository.search(id) ; if (videoOption.isEmpty) throw VideoNotFound(id) else videoOption.get } Monadic programming is imperative programming with steroids!
  • 89. 🗺 Design principles > 🦄 Functional Architectures VideoController VideoFinderRepo VideoFinder HTTPVideoClient VideoFinder… … DoobieRepo… VideoRepo LOGIC
  • 90. object FutureBasedController{ case class VideoController( videoFinder: VideoFinder[Future] )(implicit ec: ExecutionContext) extends Http4sDsl[Future] { val service = HttpService[Future] { case GET -> Root / "videos" / id => videoFinder.find(VideoId(id)) flatMap { case video => Ok(video.asJson) } recoverWith { case error: VideoNotFound => NotFound(error.asJson) } } } } ⚡Error Handling > 🦄 FP > Raise errors Future-based HTTP Mapping LOGIC
  • 91. case class VideoController[P[_]: Effect]( videoFinder: VideoFinder[P] ) extends Http4sDsl[P] { val service = HttpService[P] { case GET -> Root / "videos" / id => videoFinder.find(VideoId(id)) flatMap { case video => Ok(video.asJson) } handleErrorWith { case error: VideoNotFound => NotFound(error.asJson) } } } ⚡Error Handling > 🦄 FP > Raise errors Generic HTTP Mapping MonadError API LOGIC
  • 92. 🗺 Design principles > 🦄 Functional Architectures VideoController VideoFinderRepo VideoFinder HTTPVideoClient VideoFinder… … DoobieRepo… VideoRepo INSTANCE
  • 93. class HttpVideoFinder extends VideoFinder[Future] { def find(id: VideoId)(implicit E: MonadError[Future, VideoFinderError]): Future[Video] = Http().singleRequest(HttpRequest( method = HttpMethods.GET, uri = httpConfig.addPath("videos"))).flatMap { case HttpResponse(StatusCodes.OK, _, entity, _) => entity.dataBytes .runFold(ByteString(""))(_ ++ _) .map(_.utf8String.parseJson.convertTo[Video]) case HttpResponse(StatusCodes.NotFound, _, _, _) => E.raiseError(VideoFinderError(id)) } } ⚡Error Handling > 🦄 FP > Raise errors
  • 94. 🤔 Rethinking time % Implementation details
  • 95. Differences on APIs/layers implementation • Same goal: Decouple from infrastructure • 🦄 FP: • One step forward to avoid infrastructure leaks • Again, through type classes: a better API • More complexity: monads, applicatives, etc. % Implementation > 🤔 Rethinking time > 🏗 APIs
  • 96. Differences on error handling • 🦄 FP: • FP tell no lies (or, rather, doesn’t hide anything): more robust contracts using type system • Declarative: more abstract about how the error will be raised (exceptions, Option, Either, etc) % Implementation > 🤔 Rethinking time > ⚡Error Handling
  • 99. 🍕Takeaways ⚡ Exceptions 🏗 Monolithic interfaces 👤 Rich domain models
  • 100. 🍕Takeaways ⚡ Exceptions 🏗 Monolithic interfaces 👤 Rich domain models 🦄 TypeClasses ⚛ APIs with steroids 🧐 FP as a subsuming paradigm
  • 103. 👉Formación FP presencial 👈 hablapps.com
  • 104. 👉Cursos en vídeo online 👈 Descuento asistentes charla: Primer mes 19€: bit.ly/codelytv19e