DreamFactory: Agile Anti-Pattern with BaaS

DreamFactory Intermediate Service

Avatar von Martin Brotzeller

DreamFactory is a BaaS (Backend as a Service) that connects a multitude of data sources to APIs that apps can connect to. With our Agile Anti-Pattern App (MAPA), we wanted to move the patterns database out of the app, so that we did not have to publish a new version of the app every time we added or changed patterns. The simplest way for this was to put the complete data set (a tiered json file) on a webserver and tell the app to download from there. This would solve the problem, but be a pain to edit or version. The optimal solution would of course be a somewhat normalized database of patterns, their symptoms, their remedies, categories and a pattern-categories relation.

DreamFactory to the rescue

DreamFactory offers exactly that. Multiple databases or other sources are supported. For our relatively easy case, sqlite is sufficient. For each part of the database, API calls are automatically generated for data definition, manipulation and retrieval. Unfortunately, DreamFactory is thorough but rigid on which API calls are generated and what can be added. Since we normalized the data, all parts could be separately queried, but there was no way to get everything in one request.

A mobile app on the other hand, should be rather limited in how many network calls are made, and how much the app needs to know about the way data is stored in the database. To provide some glue, DreamFactory offers creation of services that do not run on top of any data source. As an example, the documentation implements a math service that offers a REST api for addition. Fortunately, DreamFactory can easily query it’s own APIs, so you can generate an intermediate service that aggregates API calls to yield compound data objects.

Fitting our needs

In the end we want a data structure that looks like this:

{
    "categories": [
        {
            "id": 0,
            "patterns": [
                9,
                16,
                17,
                19,
                20
            ],
            "title": "Management"
        },
        ...
    ],
    "patternDetails": [
        {
            "id": 0,
            "remedies": [
                "Nutze Audio-/Video-Tools.",
                "Erinnere Teilnehmer an anstehende Termine.",
                "Bereite Dich als Teilnehmer auf Deinen Termin vor."
            ],
            "symptoms": [
                "Meetings sind nicht vorbereitet",
                "Kommunikation läuft in parallelen Chats und Informationen aus Mimik, Gestik und sozialer Interaktion sind nicht möglich."
            ],
            "title": "Remote Daily Standup"
        },
        ...
    ]
}

First of all, we need to retrieve data from all database tables in question:

$api = $platform["api"];
$get = $api->get;
 
$url = 'antipattern/_table/antipattern';
$pattern = $get($url, NULL, $options);
 
$url = 'antipattern/_table/category';
$categories = $get($url, NULL, $options);
 
$url = 'antipattern/_table/remedy';
$remedies = $get($url, NULL, $options);
 
$url = 'antipattern/_table/symptom';
$symptoms = $get($url, NULL, $options);
 
$url = 'antipattern/_table/antipattern_category';
$pattern_categories = $get($url, NULL, $options);

Help wanted

Unfortunately DreamFactory apparently thinks joins need some kind of special permission on the database scheme, even though the role for this app has rights to antipattern.*, so we need to resolve the joins manually in PHP. If anyone knows, what is needed to be able to request

$url = 'antipattern/_table/antipattern?related=Remedies%2CSymptoms';

please let us know, we even tried to explicitly allow script access for any verb to antipattern/_table_/remedy, even though antipattern/* was already allowed for any verb for the role in question.

Back to topic

The mapaRequest object is a value object that will be used to yield the correct JSON result later. It can be instantiated, but has no logic of it’s own.

class mapaRequest {
    public $categories;
    public $patternDetails;
    public function __construct($patterns, $categories) {
        $this->patternDetails = $patterns;
        $this->categories = $categories;
    }
}

The mapaGenerator class renames some properties and joins symptoms and remedies to their respective patterns and supplements categories with their relations to patterns (see the following example). The part that says ['content']['resource'] just picks the relevant part out of the API response from the database service. After restructuring the data, some ids are offset for backwards compatibility, because the database is 1-indexed while the original JSON is 0-indexed.

class mapaGenerator{
    public static function generate($patterns, $categories, $symptoms, $remedies, $pattern_categories) {
        $pats = self::joinPatterns(
            $patterns['content']['resource'],
            $symptoms['content']['resource'],
            $remedies['content']['resource']
        );
        $cats = self::joinCategories(
            $categories['content']['resource'],
            $pattern_categories['content']['resource']
        );
        return new mapaRequest($pats, $cats);
    }
     
    public static function joinPatterns($patterns, $symptoms, $remedies) {
        $out = [];
        foreach ($patterns as $pattern) {
            $out[$pattern['id']] = [
                'id' => $pattern['id']-1,
                'title' => $pattern['name'],
                'symptoms' => [],
                'remedies' => []
            ];
        }
        foreach ($symptoms as $symptom) {
            $out[$symptom['antipattern_id']]['symptoms'][] = $symptom['text'];
        }
        foreach ($remedies as $remedy) {
            $out[$remedy['antipattern_id']]['remedies'][] = $remedy['text'];
        }
        return array_values($out);
    }
    public static function joinCategories($categories, $patterns) {
        $out = [];
        foreach($categories as $category) {
            $out[$category['id']] = [
                'id' => $category['id']-1,
                'title' => $category['name'],
                'patterns' => []
            ];
        }
        foreach($patterns as $pattern) {
            $out[$pattern['category_id']]['patterns'][] = $pattern['antipattern_id']-1;
        }
        return array_values($out);
    }
}

Finally, we just need to return the value object as a JSON response, and it yields the correct format for our app.

$mr = mapaGenerator::generate($pattern, $categories, $symptoms, $remedies, $pattern_categories);
 
return json_encode($mr);

Conclusion

This way, the MAPA app (german article) can hold the data as before, but query newer versions without needing an update. As an added benefit, we could add another app (probably a web app) that allows people to add and alter patterns in a structured way instead of by database access.

Avatar von Martin Brotzeller

Kommentare

2 Antworten zu „DreamFactory Intermediate Service“

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert


Für das Handling unseres Newsletters nutzen wir den Dienst HubSpot. Mehr Informationen, insbesondere auch zu Deinem Widerrufsrecht, kannst Du jederzeit unserer Datenschutzerklärung entnehmen.