Layers, ports & adapters - Part 1, Foreword

Posted on by Matthias Noback

Looking back at my old blog posts, I think it's good to write down a more balanced view on application architecture than the one that speaks from some of the older posts from 2013 and 2014. Before I do, I allow myself a quick self-centered trip down memory lane.

Why Symfony? Seven facts

The archive tells an interesting story about how my thoughts about software development changed over time. It all started with my first post in October 2011, How to make your service use tags. Back in those days, version 2 of the Symfony framework was still quite young. I had been working with symfony (version 1, small caps) for a couple of years and all of a sudden I was using Symfony2, and I absolutely loved this new shiny framework. I learned as much as I could about it (just as I had done with version 1), and eventually I became a Certified Symfony Developer, at the first round of exams held in Paris, during a Symfony Live conference. During those years I blogged and spoke a lot about Symfony, contributed to the documentation, and produced several open source bundles. I also wrote a book about it: A Year With Symfony.

The Naked Bundle

But then some cracks began to appear in the relationship between me and Symfony. Posts like Some things I don't like about Bundles and Unnecessary contrapositions in the new "Symfony Best Practices" are telling about this period. I struggled with the thing that I had started to love so dearly. Trying to earn back my freedom, I started frantically cutting away the framework's tentacles. This resulted in several posts, in which "framework independence" was a central concept. Amongst other things, I created a series about framework-independent controllers and talk called The Naked Bundle, which is about my quest to remove as many things as possible from the bundle, making it an almost optional concept in a Symfony application.

Package design

Now that the framework was no longer my safe haven, there was a period in which I worked on more basic programming topics, like SOLID and Package Design. In parallel to working on leaving behind Symfony as the base for everything I did, I started working on a book, Principles of PHP Package Design. Eventually, I made "PHP" an implementation detail for this book, and it was released as Principles of Package Design.

Training Tours

I don't know how it happened, but all of a sudden I was fascinated by hexagonal architecture and command buses - two things that don't necessarily belong together as I know now, but which nevertheless formed a great source of inspiration. I even started a training tour through Europe about the topic. The command bus obsession resulted in a series of blog posts as well as an open source library called SimpleBus. This library was developed at the same time as Tactician was. This library is the go-to SimpleBus alternative created by Ross Tuck, with whom I shared the obsession. We teamed up and around that time started touring together with the PHP Architecture Tour. On day 1 of the workshop I did my hexagonal training, on day 2 he did his DDD training, from which I learned quite a lot.

"Advanced Application Architecture"

Combining views, knowledge and experience, finally resulted in something that I call in my trainings an "Advanced Application Architecture". It should actually be called "Simple Application Architecture". If you're used to developing "Symfony applications", or "Laravel applications" it may look like a bunch of verbose stuff, but I've come to like it a lot.

Coming at peace with "the framework" has been quite an important event for me. I've come to realize that there's a time and a place for everything, even for the framework. That place is the Infrastructure layer and you can fully embrace any kind of RAD-stupid thing your framework offers, as long as it stays inside that layer, and nothing of it trickles down into either the Application or g*d forbid the Domain layer.

In another post I will lay out to you the basics of what today I think is a good application architecture (I'm sure it won't stop at this, but at least I wanted to document my current views somewhere...).

PHP architecture design Comments

Designing a JSON serializer

Posted on by Matthias Noback

Workshop utilities

For the workshops that I organize, I often need some "utilities" that will do the job, but are as simple as possible. Examples of such utilities are:

  • Something to dispatch events with.
  • Something to serialize and deserialize objects with.
  • Something to store and load objects with.
  • Something to use with event sourcing (an event store, an event-sourced repository).

I put several of these tools in a code base called php-workshop-tools. These utilities should be small, and very easy to understand and use. They will be different from the frameworks and libraries most workshop participants usually use, but they should offer more or less the same functionality. While designing these tools, I was constantly looking for the golden mean:

  • Make the tool generic, but don't support every imaginable use case.
  • Add support for proper dependency injection, but provide static/singleton/function-based helpers.

I have already described my thoughts about usability versus proper design of utility objects in another post: The case for singleton objects, fa├žades, and helper functions. In this post I'd like to look closer at some of the design considerations for the JSON serializer I eventually came up with.

The serializer utility class hasn't become part of the workshop tools code base. It lives in its own code base, as I thought it deserves its own project (with a few tweaks it might some day become really useful outside of my workshops). I called the serializer "naive serializer" as I believed it to be a bit dumb at first.

Use cases, requirements

In my applications I use serialization mainly to:

  1. Take some plain text structured data (e.g. JSON, XML) and transform it into some object that I can use in my application. Such an object has its own type and predefined properties, but it has no behavior. I use it to let data travel to a deeper layer in my application. In other words, such an object is a Data Transfer Object (DTO).
  2. Take some event objects and serialize them, in order to persist them in an event store or publish them to a queue.
  3. Take some view object (which is also a DTO) and serialize it as a response to some API query.

Considering only these use cases allowed me to keep the naive-serializer pretty simple. It doesn't need to have some of the features that other major serializers have, like:

  1. Using getters or setters to retrieve or update values.

    I don't perform (de)serialization directly from or to entities. That means, "domain invariants" won't have to be protected while deserializing, as this will be done properly by the domain objects themselves in a later stage (as if an anemic domain model could perform this kind of protection anyway). Data Transfer Objects don't have to "protect" themselves, so we can just copy the data right into it. I do recommend using a schema validator though, to make sure the plain text data you provide has the right structure.

  2. Resolving subclasses using discriminator maps, etc.

    This is a pretty invasive feature that comes with a lot of trouble in terms of code complexity. I think there are valid use cases for it, but I assume that they are not within the most common ones (at least not in my career).

  3. Support for custom "handlers", for \DateTime objects, etc.

    Other serializers have this feature because for one, PHP's built-in \DateTime[Immutable] objects can be serialized:

    {"date":"2017-07-11 08:14:31.379653","timezone_type":3,"timezone":"UTC"}
    

    But this string can not be deserialized by simply instantiating an empty instance of \DateTime[Immutable] and then repopulating the attributes.

    I believe this is a bit weird, but I also believe it can be easily circumvented (and should be anyway, in my opinion), by not using \DateTime[Immutable] as a primitive type in your domain model. I prefer to use a wrapper value object like:

    final class Timestamp
    {
        /**
         * @var string
         */
        private $timestamp;
    
        private function __construct(string $timestamp)
        {
            $this->timestamp = $timestamp;
        }
    
        public static function fromDateTimeImmutable(\DateTimeImmutable $timestamp): Timestamp
        {
            return new self($timestamp->format(\DateTime::ATOM));
        }
    
        public function asDateTimeImmutable(): \DateTimeImmutable
        {
            return \DateTimeImmutable::createFromFormat(\DateTime::ATOM, $this->timestamp);
        }
    
        public function __toString(): string
        {
            return $this->timestamp;
        }
    }
    

    This approach forces you to think of an internal value for the object that uniquely determines the value it represents, which I find beneficial to the design of the object itself.

    If you don't allow complicated values like \DateTime[Immutable], the (de)serialization algorithm becomes much simpler as you won't need to write or allow any custom handler code anymore.

  4. Custom configuration (e.g. annotations) to indicate the type of a property, like this:

    /**
     * @Serializer\Type("string")
     * @var string
     */
    private $foo;
    

    After dropping the support for custom handlers (see the previous point), it's now easy to limit the possible types for properties. In fact, we can limit the list of supported types to those already supported and recognized by PHP, or slightly broader, those used in @var and @return annotations recognized by PHPDocumentor. By the way, there is an accompanying library implementing type resolving for @var annotations, which turned out to be very useful for my own project: phpdocumentor/reflection-docblock.

    This allowed me to drop the need for extra configuration on top of existing @var annotations. In fact, I started relying on those, forcing users to add them, assuming many developers already do. Since we won't be able to serialize a value of type resource for example, this limits the list of supported property types to:

    • null
    • scalar (int, float, bool)
    • user-defined classes
    • arrays where every value is of the same type (maps or lists)
    • and any combination of the above

    If PHP ever comes with support for type declarations for properties, I assume that the need for relying on @var annotations will disappear.

  5. Custom configuration (e.g. annotations) to indicate which values should be included or excluded, like this:

    /**
     * @Serializer\Exclude("ALL")
     */
    class Foo
    {
        /**
         * @Serializer\Include
         */
        private $bar;
    }
    

    In practice this kind of configuration is often used when objects are a bit confused about the roles they play. It's the same for form validation groups by the way. Most often objects like these are either anemic domain entities, or they are fulfilling both command and query responsibilities. I recommend using different DTOs for different write and read-related use cases anyway, so I didn't want to make this part of the specification. If you find yourself wanting to skip some object-internal properties, like caches, etc. you can simply create another object, one that contains none of the tricky stuff, and serialize that.

  6. Custom configuration (e.g. annotations) to indicate how the name of a property should be converted to the name of a JSON object identifier, like this:

    /**
     * @Serializer\NamingStrategy("SnakeCase")
     */
    class Foo
    {
        /**
         * @Serializer\SerializedName("bazzzz")
         */
        private $bar;
    }
    

    In practice, such a feature is used for some kind of information hiding, where we don't want to expose the names of our properties. I think it's better to just rename the property anyway, or create a new object with the right property names after all. By the way, if you like "snake case", you can always name your properties in that style too. So the "naive serializer" simply uses the real property names and provides no transformation options.

Implementing the serializer

All of the reasoning that went into the project so far allowed me to define the following list of design guidelines:

  • Users shouldn't be forced to add custom configuration to their existing classes.
  • Users shouldn't need to write any supporting code.
  • The solution should take care of as few edge cases as possible.
  • The solution should be as small as possible, without becoming useless (<=100 LOC).
  • The solution should warn the user about its limitations using descriptive exceptions.

This list was pretty helpful as it really helped me to focus. Whenever I had to make some decision while writing the code, I could always use these guidelines to make the do the right thing.

I defined a class with all the cases I wanted to support:

final class SupportedCases
{
    /**
     * @var string
     */
    public $a;

    /**
     * @var int
     */
    public $b;

    /**
     * @var SupportedCases[]
     */
    public $c = [];

    /**
     * @var bool
     */
    public $d;

    /**
     * @var float
     */
    public $e;
}

Then I defined that the expected JSON output should be:

{
    "a": "a",
    "b":1,
    "c": [
        {
            "a": "a1",
            "b": 2,
            "c": [],
            "d": null,
            "e": null
        }
    ],
    "d": true,
    "e": 1.23
}

Deserializing the JSON data should result in an object equal to the one we just serialized and this of course is a perfect starting point for a unit test (maybe more like a component test). After I implemented the "happy path", I added some checks and assertions here and there to make the serializer fail in more explicit and developer-friendly ways.

Conclusion

I wanted to write about this project, not because I want you to use the serializer (I guess you shouldn't; something I didn't do is performance optimization for example). I simply wanted to describe how a bit of thinking can help drastically limit the scope of a project. Why are frameworks and libraries (for serialization, for forms, for persistence) so big and complicated? Because they want or need to support every imaginable use case. I find it very inspiring that the Doctrine team is currently dropping support for several features, in order to ease maintenance, and keep the library more focused. Removing features like detaching and merging entities will make the code much simpler. And removing support for Yaml configuration will prevent future bugs (apparently there has been a lot of trouble with it in the past).

I also wanted to describe some of the ways in which design issues on the user's side can lead to complicated feature requirements. Once you've fixed design issues like confused object roles, anemic domain models, lack of CQRS, etc. you won't need most of the special features which existing serializers are offering. You can keep the "happy path" of the code pretty clean, reducing code complexity. By effect, maintenance will be easier. In fact, you won't have too many users complaining about things that don't work. Particularly so if you throw clear exceptions when the library is used in an appropriate way.

Finally, I experienced again that posing some limitations can make you more creative. I have encountered this principle in several creative/artistic contexts before. In this case it was: aim for less than 100 lines of code (LOC). Of course, it code quality shouldn't be sacrificed for LOC. Also, you should always keep questioning the limitations themselves. But aiming for a small solution helped me cut a lot of waste from the code. Only after all the tests were green and the edge cases had been covered, I allowed myself some breathing space and expanded the code a bit to improve readability. The result is - I think - a pretty simple, clean, yet useful serializer.

PHP serializer Comments

How to make Sculpin skip certain sources

Posted on by Matthias Noback

Whenever I run the Sculpin generate command to generate a new version of the static website that is this blog, I notice there are a lot of useless files that get copied from the project's source/ directory to the project's output/ directory. All the files in the output/ directory will eventually get copied into a Docker image based on nginx (see also my blog series on Containerizing a static website with Docker). And since I'm on a hotel wifi now, I realized that now was the time to shave off any unnecessary weight from this Docker image.

My biggest mistake was not googling for the quickest way to skip certain sources from getting copied to output/. Instead, I set out to hook into Sculpin's event system. I thought it would be a good idea to create an event subscriber and make it subscribe to the Sculpin::EVENT_BEFORE_RUN event. Event subscribers for this event will receive a so-called SourceSetEvent, allowing them to mark certain sources as "should be skipped".

Sculpin is built on many Symfony components and it turned out to be quite easy to set up a traditional event subscriber, which I called SkipSources:

final class SkipSources implements EventSubscriberInterface
{
    /**
     * @var string[]
     */
    private $patterns = [];

    public function __construct(array $patterns)
    {
        $this->patterns = $patterns;
    }

    public function skipSourcesMatchingPattern(SourceSetEvent $event): void
    {
        // see below
    }

    public static function getSubscribedEvents(): array
    {
        return [
            Sculpin::EVENT_BEFORE_RUN => ['skipSourcesMatchingPattern']
        ];
    }
}

You can create your own Symfony-style bundles for a Sculpin project, but in this case defining a simple service in sculpin_kernel.yml seemed to me like a fine option too:

# in app/config/sculpin_kernel.yml

services:
    skip_sources:
        class: SculpinTools\SkipSources
        arguments:
            # more about this below 
            - ["components/*", "_css/*", "_js/*"]
        tags:
            - { name: kernel.event_subscriber }

Due to the presence of the kernel.event_subscriber tag Symfony will make sure to register this service for the events returned by its getSubscribedEvents() method.

Looking for a way to use glob-like patterns to filter out certain sources, I stumbled on the fnmatch() function. After that, the code for the skipSourcesMatchingPattern() method ended up being quite simple:

foreach ($event->allSources() as $source) {
    foreach ($this->patterns as $pattern) {
        if (fnmatch($pattern, $source->relativePathname())) {
            $source->setShouldBeSkipped();
        }
    }
}

It matches a source with each of the patterns based on the source's relative pathname, as nothing outside of the source/ directory is relevant anyway. The patterns themselves are passed in as the event subscriber's first constructor argument. It's simply a list of glob-like string patterns.

My solution turned out to be quite an effective way to mark certain files as "should be skipped", which was my goal.

La grande finale

Just like in my previous blog post, I finally ran into another possible solution, that's actually built in to Sculpin - a simple ignore configuration key allowing you to ignore certain sources using glob-like patterns. It does use a rather elaborate pattern matching utility based on code from Ant. Not sure if this library and fnmatch() have "feature parity" though.

Turns out, all my extra work wasn't required after all. A simple Google search would have sufficed!

So I removed all of this code and configuration from my project. But I still wanted to share my journey with you. And who knows, it could just be useful to have an example lying around of how to register an event subscriber and hook into Sculpin's build lifecycle...

PHP Sculpin Comments