RAD with Symfony

Renato Mefi @renatomefi

40 Minutes!

To lunch

RAD with Symfony


query radWithSymfony {
	person(id: "renatomefi") {
		name
		twitter
        company
		bioPerLine
	}
	talk(id: "rad-symfony") {
	    title
	    date
	    type
	}
}
                    

{
  "data": {
    "person": {
      "name": "Renato Mendes Figueiredo",
      "twitter": "@renatomefi",
      "company": "@usabilla",
      "bioPerLine": [
        "ZCE, ZCPE, ZFCE, LPIC-1, LFCS, LFCE Professional",
        "a.k.a.: A guy who loves linux and software dev!",
        "Co-organizer of @PHPAmersfoort and @rumoazcephp",
        "Maintainer of php-vcr and OverblogGraphQLBundle"
      ]
    },
    "talk": {
      "title": "RAD With Symfony",
      "date": "2018-06-08T12:40:00.042Z",
      "type": "fwdays 2018"
    }
  }
}
                    

RAD

Radical?

Short for radial?

Rapid

Application

Development

Response to waterfall

RAD framework characteristics

  • Easy bootstrap
  • IDE integrations
  • Easy CI
  • Easy packing/deployment

A good RAD framework?

  • Clear Structure (Config, code, tests, ...)
  • Able to un-magic
  • Comprehensive integrations
  • Predictable behavior

Spring Boot v2+

Symfony 4 is finishing support Jan/2019

Please use 4.1+

Easy Bootstraping


    # git clone https://github.com/spring-projects/spring-boot.git ## Or spring roo/cli
    # cd to a sample
    # mvn package && java -jar target/gs-spring-boot-0.1.0.jar
                
Completely running, in memory data storage with Spring Data

Really good samples

Web servers, sockets, cli (consumers/publishers)

Rest API w/o Hateoas

Kafka, amqp, redis

Neo4J, Solr, ElasticSearch, Cassandra, CouchBase

Easy Bootstraping


    # composer create-project symfony/skeleton my-project && cd my-project
    # php -S 127.0.0.1:8000 -t public ## or use symfony server
                

Symfony Flex Recipes !== Code Samples

  • Does configure the library/bundle
  • Does NOT provide a working example
  • Exceptions to that: Logging, debugging...

Although

Maker bundle


    composer require maker --dev
                

Possible to create your own maker

  • Extend AbstractMaker
  • tag maker.command

IDE Integration

IDE Integration

It's nice

  • Dependency Injection
  • Configuration helpers: routes, di...
  • Doctrine Annotations (separate plugin)

simple-phpunit

Project Structure

Project Structure

  • bin/console is embed
  • Clearer configuration

Unmagicability


    package sample.hateoas;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication <===========
    public class SampleHateoasApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SampleHateoasApplication.class, args);
        }
    
    }

                

(un)Magic(ability)

Dependency injection

Before auto wiring/Class identifiers


    app.mailer.default:
        class:     Mailer
        arguments: ['%mailer.transport%']

    app.controller.hello:
        class: \App\Controller\HelloController
        arguments: ['@mailer']
    
                    

    app.repository.user:
        class: \App\Repository\UserRepository

    app.service.process.hello:
        class: \App\Services\HelloProcessor
        arguments: ['@app.repository.user']

    app.controller.hello:
        class: \App\Controller\HelloController
        arguments: ['@app.service.process.hello']
    
                    

With Auto wiring


    services:
        _defaults:
            autowire: true
        App\:
            resource: '../src/*'
            exclude: '../src/{Entity,Migrations,Tests,Kernel.php}'
    
                    

    final class RepositoryA
    {
        ...
    }
    
    final class ServiceA
    {
        ...
        public function __construct(RespositoryA $repositoryA)
        {
            $this->repository = $repositoryA;
        }
        ...
    }
                    

    final class ControllerA
    {
    ...
        public function __construct(ServiceA $serviceA)
        {
            $this->service = $serviceA;
        }
    ...
    }
    
                    

Rename App namespace

Text/Replace App *.php


    "autoload": {
        "psr-4": {
            "Fwdays\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "FwdaysTests\\": "tests/"
        }
    },
                    

    Fwdays\:
        resource: '../src/*'
        exclude: '../src/{Entity,Migrations,Tests,Kernel.php}'
    
                    

Structure consciousness


    Fwdays\Application:
        resource: '../src/Domain/*'

    Fwdays\Domain:
        resource: '../src/Domain/*'

    Fwdays\Infrastructure:
        resource: '../src/Infrastructure/*'
    
                    

"The Model"


    // src/Model/User.php

    class User
    {
        /**
         * @Assert\NotBlank()
         * @Assert\Type("string")
         */
        public $name;
    }

                    

Simple VO


    // src/Domain/User.php

    final class User
    {
        private $name;

        public function __construct(string $name) {
            Assert::notBlank($name);
            $this->name = $name;
        }
    }

                    

More Domain


    final class Name
    {
        private $value;

        public function __construct(string $value) {
            Assert::notBlank($name);
            $this->value = $value;
        }

        public function getName(): string
        {
            return $this->value;
        }
    }

                    

    final class User
    {
        private $name;
        private $email;

        public function __construct(Name $name, Email $email) {
            $this->name = $name;
            $this->email = $email;
        }
    }

                    

deptrac


    # depfile.yml
    paths:
        - ./src
    exclude_files:
        - .*test.*
    layers:
        - name: Controller
        collectors:
            - type: className
            regex: .*Controller.*
        - name: Repository
        collectors:
            - type: className
            regex: .*Repository.*
        - name: Service
        collectors:
            - type: className
            regex: .*Service.*
    ruleset:
        Controller:
        - Service
        Service:
        - Repository
        Repository: ~
                    

Workflow component


    $definitionBuilder = new DefinitionBuilder();
    $definition = $definitionBuilder->addPlaces(['opening', 'talks', 'lunch', 'speakers_panel'])
        ->addTransition(new Transition('to_talks', 'opening', 'talks'))
        ->addTransition(new Transition('hungry', 'talks', 'lunch'))
        ->addTransition(new Transition('cool', 'talks', 'speakers_panel'))
        ->build()
    ;
                    

    $workflow->apply($you, 'to_talks');
    $workflow->apply($you, 'hungry');
    $workflow->apply($you, 'cool'); // TransitionException

                    

Messenger component

Are we on the right path?

Start less opinionted

Think about your architecture

Tools/Components

Thank you! Any Questions?

http://talks.mefi.in/rad-symfony-fwdays2018