Solving The “Widget Problem” In ADR

The “widget problem” is when you have several panels or content areas on an HTML page that have different data sources. You might have a main content area, then a calendar off to the side, with perhaps a list of recent news items or blog posts, a todo or reminder widget, and maybe other information panels. The problem is that they each have different data sources, and may not always be displayed in every circumstance — perhaps they are only shown to some users based on their preferences, or under certain conditions.

So how, in Action-Domain-Responder, do we get the “right” data for the set of widgets that are actually going to be displayed? (We’ll presume here that the entire page is being rendered server-side, for delivery as a whole to the client.)

The answer is “the same as with anything else” – we just have more kinds of data to get from the domain. The domain has all the data needed, and the knowledge necessary to figure out which data elements to return.

Let’s start with the Action, which is intentionally very spare: it only collects input, calls the Domain with that input, then invokes the Responder:

<?php
class PageAction
{
    public function __construct(
        PageService $domain,
        PageResponder $responder
    ) {
        $this->domain = $domain;
        $this->responder = $responder;
    }

    public function __invoke(HttpRequest $request)
    {
        $payload = $this->domain->fetchPageData(
            $request->getAttribute('sessionId'),
            $request->getAttribute('pageName')
        );
        return $this->responder->respond($request, $payload);
    }
}

The domain work is where the heavy lifting happens. The example below returns the domain objects and data wrapped in a Domain Payload object.

<?php
class PageService
{
    // presume $userService, $sessionService, and $database
    // dependencies are injected via constructor

    public function fetchPageData($sessionId, $pageName)
    {
        $session = $this->sessionService->resume($sessionId);
        $user = $this->userService->fetch($session->userId);

        // the main page data
        $mainData = $this->fetchMainData($pageName);
        if (! $mainData) {
            return new Payload('NOT_FOUND');
        }

        // an array of widgets to show
        $widgets = [];

        // different users might prefer to see different widgets
        foreach ($user->getWidgetsToShow() as $widgetName) {
            $method = "fetch{$widgetName}Data";
            $widgets[$widgetName] = $this->$method();
        }

        $this->sessionService->commit($session);

        return new Payload('FOUND', [
            'user' => $user,
            'page_name' => $pageName,
            'main_data' => $mainData,
            'widgets' => $widgets
        ]);
    }

    protected function fetchMainData($page_name)
    {
        return $this->database->fetchRow(
            "SELECT * FROM pages WHERE page_name = ? LIMIT 1",
            $page_name
        );
    }

    protected function fetchTodoData() { ... }

    protected function fetchRemindersData() { ... }

    protected function fetchUpcomingEventsData() { ... }

    protected function fetchCalendarData() { ... }

}

Finally, the Responder work becomes as straightforward as: “Is there Todo data to present? Then render the Todo widget via a Todo helper using the Todo data.” It could be as simple as this:

<?php
class PageResponder
{
    // ...

    public function respond(Request $request, Payload $payload)
    {
        if ($payload->getStatus() == 'NOT_FOUND') {
            return new Response(404);
        }

        $output = $payload->getOutput();

        $html = '';
        $html .= $this->renderHeader($output['page_name'];
        $html .= $this->renderNav();
        $html .= $this->renderMain($output['main_data']);

        foreach ($output['widgets'] as $widgetName => $widgetData) {
            $method = "render{$widgetName}Html";
            $html .= $this->$method($widgetData);
        }

        return new Response(200, $html);
    }

    protected function renderHeader($request, $pageName) { ... }

    protected function renderNav($request) { ... }

    protected function renderMain($request, $mainData) { ... }

    protected function renderTodoHtml($request, $widgetData) { ... }

    protected function renderRemindersHtml($request, $widgetData) { ... }

    protected function renderUpcomingEventsHtml($request, $widgetData) { ... }

    protected function renderCalendarHtml($request, $widgetData) { ... }
?>

One alternative here is for some client-side Javascript to make one additional call per widget or panel to retrieve widget-specific data, then render that data on the client. The server-side work becomes less complex (one action per widget, and transform the data to JSON instead of HTML) – but the client-side work becomes more complex, and you have more HTTP calls back-and-forth to build the page.

Avoid Dependency Injection

At least, avoid it when building DDD Aggregates:

Dependency injection of a Repository or a Domain Service into an Aggregate should generally be viewed as harmful. The motivation may be to look up a dependent object instance from inside the Aggregate. The dependent object could be another Aggregate, or a number of them. … Preferably, dependent objects are looked up before an Aggregate command method is invoked, and passed into it.

… Take great care not to add unnecessary overhead that could be easily avoided by using other design principles, such as looking up dependencies before an Aggregate command method is invoked, and passing them into it.

This is only meant to warn against injecting Repositories and Domain Services into Aggregate instances. Of course, dependency injection is still quite suitable for many other design situations. For example, it could be quite useful to inject Repository and Domain Service references into Application Services.

— “Implementing Domain Driven Design”, Vaughn Vernon, p 387.


On a related note, regarding where Entity validation logic goes, we have this …

Validation is a separate concern and should be the responsibility of a validation class, not a domain object.

— ibid., p 208

… and this …

Embedding validation logic inside an Entity give it too many responsibilities. It already has the responsibility to address domain behavior as it maintains its state.

— ibid., p 212

… but then we see this:

How do clients ensure that Entity validation occurs? And where does validation processing begin? One way places a validate() method on all Entities that require validation. … Any Entity can safely have its validate() method invoked. …

However, should Entities actually validate themselves? Having its own validate() method doesn’t mean the Entity itself performs validation. Yet, it does allow the Entity to determine what validates it, relieving clients from that concern:

public class Warble extends Entity {
    ...
    @Override
    public void Validate(ValidationNotificationHandler aHandler) {
        (new WarbleValidator(this, aHandler)).validate();
    }
}

… The Entity needs to know nothing about how it is validated, only that it can be validated. The separate Validator subclass also allows the validation process to change at a diferent pace from the Entity and enables complex validations to be thoroughly tested.

— ibid., p 214-215

It seems like a small step after that to inject a fully-constructed validation object into the Entity at construction time, and have the validate() method call that, instead of creating a new validation object inside the Entity.