DDD + BDD in a YOLO (Symfony2 Components) setup Series: The Renaissance – Part I

I ran into many videos and resources for DDD and have been reading quite a bit. Also doing hands on. I am writing this blog series because I believe we have been incepted. There is chaos, scattered, scant sometimes, and misinformation about DDD and nobody has stepped down to a newbie level yet to explain in a practical way what is going on with the architectures that had been in the works for other languages since the golden era. I am realizing that the PHP world is experiencing this renaissance because the level of many developers have matured in such a way that techniques from the Java world are making renaissance inroads with them in order to cope with PHP enterprise application and smaller applications designed smarter and cleaner.

Let’s begin using a popular tool to illustrate this, then explain what is this, just bear with me:

title DDD Exam Process Interactor
 
participant client
participant http
participant interactor
participant factory
participant repository
participant domain-services
 
client->http: Http Request\n (answer question)
http->interactor: AnswerView
activate interactor
interactor->factory: createFromView(AnswerView)
factory->interactor: Answer
interactor->repository: Answer(s)
repository->interactor: Question(s)\ncreateFromData()
interactor->domain-services: othersMethods()
domain-services->interactor: results
 
interactor->http: QuestionResponse
deactivate interactor
http->client: Http Response

DDD Exam Process Interactor

The Domain
Let’s suppose I am solving an exam questionary problem. My domain is the dealing with exams. From the student point of view is the taking of the exams. From the professor is the creation of those exams. Maybe from the university point of view is grading and administering who to ban or choose for given exams, etc.

The Renaissance
Thanks to several resources and friends I am walking through the learning curve of DDD + BDD now. For me this is a renaissance of the design culture in the PHP world. To describe then what the procedure I am employing is let me focus on the graphic above.

I have grabbed the itent of the student, he or she take an exam and the first thing they know is they are presented with question after question. The question prompts them for an answer and after they have responded then they are given the next question. I writing a behat feature, create a default context from where I use helper class trying to reach a DSL-like helper. Then as I am trying to make pass the feature I create collateral objects thinking always in terms of intent and not in terms of doctrine entities. I don’t want to know anything about doctrine, about a component library or even a framework. Yes I said it right, I hate, I can’t see doctrine and Symfony2 at this point in time. With this pristine mindset then we make the behat feature pass and iterate of course with phpspec on the inner circle to make the phpspec specs pass as well. This is in the spirit of outside-in approach of BDD acceptance and specification. After this is done, things are not there yet. This was a spike and we should relax. Now we need to organize our ideas and rename folders and files and start making sense on the actual model we are creating by giving thought and digging in DDD theory and practice.

What happens here in the diagram above, it explains how the feature goes in terms of transaction like and domain like ideas. Student issues with his or her browser an HTTP request, this is translated to a boundary acceptable request that plugs into the ports/boundary of our domain model. Because it is an answer, the proper DTO/port is an object of type AnswerPort. I have changed the naming here from view to port because I think it makes more sense. All this inputs land on an interactor that bears as collaborators the services, factories and repositories needed to manipulate the domain model and give an application response corresponding to another port. This port gets translated back into an HTTP response and this is sent back to the student, that is another question and some collateral information.

Here is the feature:

Feature: student can take an exam
 
  Scenario: student takes an exam             # features/user_stories.feature:3
    Given student starts an exam              # FeatureContext::student_starts_an_exam()
    And student is presented with a question  # FeatureContext::student_is_presented_with_a_question()
    When student answers to question          # FeatureContext::student_answers_to_question()
    Then student is presented with a question # FeatureContext::student_is_presented_with_a_question()
    When student answers to question          # FeatureContext::student_answers_to_question()
    Then student is presented with a question # FeatureContext::student_is_presented_with_a_question()
    When student answers to question          # FeatureContext::student_answers_to_question()
    Then student ends exam and gets graded    # FeatureContext::student_ends_exam_and_gets_graded()

And this is how I have arranged the project files:

~ tree                                                                                                          
.
└── Domain
    ├── Model
    │   └── Exam
    │       ├── ExamSpec.php
    │       ├── OptionSpec.php
    │       └── QuestionSpec.php
    ├── Port
    │   ├── ExamSpec.php
    │   └── ExamsSpec.php
    ├── Services
    │   ├── ExamRepositorySpec.php
    │   ├── ExamsConverterSpec.php
    │   └── PrototypeManagerSpec.php
    └── UseCase
        ├── ShowsAllExamsSpec.php
        └── ShowsNextQuestionSpec.php

Notice the ports or boundaries (in this case, if we use commands then things change a bit), the domain services (sometimes get confused by application services but should not), and the model entities and value objects. The interactors are also renamed as UseCase’s. I credit here my mentors @igorw, @davedevelopment, and @beau for all the ideas, correction and advice given so far on my first steps.

So I will stop here and leave you a bit hungry for the next series. I appreciate ya’lls support getting the book, donating and just mentoring me along this road. I hope I can give all that I know at the talk in Argentina. If you are interested please contribute with new ideas below in the comments and on the repo I have on github.

I say until next for now. Graciously undeserving all of this.

your friend @cordoval.

PHPSpec Practices with Symfony2 Controllers: Part VI

So in response to the recent series Pete has started here I cloned his repo and was playing with what I have been seeing around.

Previous episodes:

I will explain it here step by step:

1. It was suggested that instead of BlogManager for the example shown we should use BlogRepository since it was more DDD friendly and more conveying the model we had so far. However we did this, I believe the correct model should not be even BlogRepository in the sense of Doctrine but in the sense of Model Driven Development which calls for this layer to be completely implementation agnostic.

2. Notice we have moved everything to annotations instead of typehinting. This specs don’t need typehinting in the sense of final classes but they do need readability. It seems to me the annotations are exactly more conveying than the typehinting which clog the signatures. I also don’t like `shoulds` on the method names.

<?php
 
namespace spec\Peterjmit\BlogBundle\Controller;
 
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Symfony\Component\HttpFoundation\Response;
 
class BlogControllerSpec extends ObjectBehavior
{
    /**
     * @param \Doctrine\ORM\EntityRepository $blogRepository
     * @param \Symfony\Bundle\FrameworkBundle\Templating\EngineInterface $templating
     */
    function let($blogRepository, $templating)
    {
        $this->beConstructedWith($blogRepository, $templating);
    }
 
    function it_is_initializable()
    {
        $this->shouldHaveType('Peterjmit\BlogBundle\Controller\BlogController');
    }
 
    /**
     * @param \Doctrine\ORM\EntityRepository $blogRepository
     * @param \Symfony\Bundle\FrameworkBundle\Templating\EngineInterface $templating
     */
    function it_responds_to_index_action($blogRepository, $templating)
    {
        $response = new Response();
 
        $blogRepository->findAll()->willReturn(['An array', 'of blog', 'posts!']);
 
        $templating
            ->renderResponse(
                'PeterjmitBlogBundle:Blog:index.html.twig',
                ['posts' => ['An array', 'of blog', 'posts!']]
            )
            ->willReturn($response)
        ;
 
        $response = $this->indexAction();
 
        $response->shouldHaveType('Symfony\Component\HttpFoundation\Response');
    }
 
    /**
     * @param \Doctrine\ORM\EntityRepository $blogRepository
     * @param \Symfony\Bundle\FrameworkBundle\Templating\EngineInterface $templating
     */
    function it_shows_a_single_blog_post($blogRepository, $templating)
    {
        $response = new Response();
 
        $blogRepository->find(1)->willReturn('A blog post');
 
        $templating
            ->renderResponse(
                'PeterjmitBlogBundle:Blog:show.html.twig',
                Argument::withEntry('post', 'A blog post')
            )
            ->willReturn($response)
        ;
 
        $this->showAction(1)->shouldReturn($response);
    }
 
    /**
     * @param \Doctrine\ORM\EntityRepository $blogRepository
     */
    function it_throws_an_exception_if_a_blog_post_doesnt_exist($blogRepository)
    {
        $blogRepository->find(999)->willReturn(null);
 
        $this
            ->shouldThrow('Symfony\Component\HttpKernel\Exception\NotFoundHttpException')
            ->duringShowAction(999)
        ;
    }
}

3. Going CAS (controllers as services) is a given already and it is natural in specking with model driven development in mind. In this case we define a controller service to which we inject the blog repository service and the templating engine service. Notice the repository blog service gets injected the entity manager, not a beautiful thing since this is already an implementation detail, however it tells us that we need to pull metadata mapping information from some model, so that is the right hint. However it would have been nicer rather to have a gateway layer. We of course define this metadata in the constructor of the repository to keep it for now in one place as shown below:

services:
    peterjmit_blog.controller.blog:
        class: Peterjmit\BlogBundle\Controller\BlogController
        arguments:
            - @peterjmit_blog.repository.blog
            - @templating
 
    peterjmit_blog.repository.blog:
        class: Peterjmit\BlogBundle\Repository\BlogRepository
        arguments:
            - @entity_manager

This is how the repository service looks like:

<?php
 
namespace Peterjmit\BlogBundle\Repository;
 
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Mapping\ClassMetadata;
 
class BlogRepository extends EntityRepository
{
    public function __construct($em)
    {
        $entityName = 'Peterjmit\BlogBundle\Model\Blog';
        $class = new ClassMetadata($entityName);
 
        parent::__construct($em, $class);
    }
}

4. We don’t need to pass to the spec methods a Response object. It is not mocking that that object does but stubbing. There is a difference I think. And I rather stub it and create the object by hand when it is needed as shown in the first piece of code above.

Here is the diff of the commits I PR to the original repo, great job so far!

Credits to Pete for making things simple and prompting me to respond avidly!

About applied phpspec on crud, there is ResourceBundle from Sylius who does a great job. I use it and explain some things related to Symfony2 Ecommerce on the Lean Book http://pilotci.com.

Let’s keep specking!

I don’t understand PHP beaurocracy but I do understand Anthony Ferrara!

This blog post is just an admonition for the PHP people involved in working out the details of our great language and community. I am talking about https://wiki.php.net/rfc#withdrawn.
Yes is an admonition to just get across one point. I can understand Anthony Ferrara’s hard work in doing 5 RFCs and passion for teaching the language in his blog and video series on youtube. I cannot understand why hard work is met with something unknown to me that discourages him from keep working on this for the community.

Something is wrong here. And whatever that unknown thing is, I think is plain wrong if it ends up discouraging one of the top experts and hard worker person in the community. I met Anthony in Miami, he probably does not remember me but after the conference was kind of over I sat on his table and introduced myself, he is all very kind and ready to involve in rich smart conversations. I met him again in Portland where I would just stand next to him, @igorw, @asm89, and @beberlei, and I would just listen to him explaining with excitement his advancements in low level and high level PHP, C and what not stuff. I do not pay for conferences anymore, I pay to get the chance to fly out from Peru, land in places like these and learn from these giants. I even sent a PR to one of his repos in which we strongly disagree about God.

However I think that something unknown that can discourage someone learning with passion like that should be dealt with for fairness sake.

So there is my blog post, my vote, and hoping Anthony comes back, keep working on those RFCs and be part of our community.

Give a retweet to get this point across if you share it! I have learned tons from this guy.

Make No JS Mess in Your Symfony2 Project: Use component

Instead of doing a mess of the repo, I found it is more clean to just use composer and component-installer from @robloach and fetch the highcharts.js library for instance:

{
    // ...
    "config": {
        // ....
        "component-dir": "web/components"
    },
    "require": {
        // ...
        "component/highcharts": "3.0.5"
    },
    "repositories": [
        {
            "type": "package",
            "package": {
                "name": "component/highcharts",
                "type": "component",
                "version": "3.0.5",
                "source": {
                    "url": "https://github.com/highslide-software/highcharts.com.git",
                    "type": "git",
                    "reference": "v3.0.5"
                },
                "extra": {
                    "component": {
                        "scripts": [
                            "js/highcharts.src.js"
                        ]
                    }
                },
                "require": {
                    "robloach/component-installer": "*"
                }
            }
        }
    ]
}

From here on you can just get the js file on your asset lines. Small trick, hope you appreciate it.

Encouragements in all good,

@cordoval