Context

https://github.com/jonmaxmue/context/tree/main

Was ist „Context“? #

  • Zweck: Der Context liefert eine wiederverwendbare Applikations‑Infrastruktur: State‑Management, Controller‑Wiring, Repositories, IO über Directus, Reaktivität (MobX), Routing‑Hooks und einen Provider für deine UI.
  • Prinzip: Du definierst deine Domäne (eigene Models, optionale Controller, eigene UI). Der Context übernimmt den Rest (Core‑Logik).
  • Nutzen:
    • Schneller Start: Plug & Play – nur Provider einbinden und moduleConfig definieren.
    • Konsistenz: Einheitliches Schema für Models und Controller, abgestimmt auf Directus.
    • Reaktivität: MobX + observer aktualisieren deine UI automatisch.
    • Trennung: Fachlogik (Models/Controller) bleibt sauber getrennt von Infrastruktur (Repos/IO/Navigation).

Wichtige Bausteine im Projekt #

  • Provider: providers/ContextControllerProvider.tsx
    Bindet den Context global ein und macht ihn per Hook verfügbar:
    • Hook: useContextControllerProvider() liefert dir den controller.
    • Beispiel im Code: ContextControllerProvider erstellt new Controller(moduleConfig, routeConfig, routerFunctions) und rendert erst, wenn controller.loaded true ist.
  • Modul‑Konfiguration: configs/moduleConfig.ts
    Deklariert Models (Collection, Klassentyp) und initialisiert Controller. Alle Module sind darüber einheitlich verdrahtet.
  • Routingconfigs/routeConfig.ts + Expo Router
    ContextControllerProvider injiziert router.push/replace in den Context, damit Controller navigieren können.
  • Reaktivität: MobX (makeAutoObservable/makeObservable)
    Models sind observabel, Controller kapseln Lade‑/Mutationslogik. In der UI verwendest du mobx-react’s observer.

Eigene Controller und Models mit Context integrieren #

Mit Context kannst du eigene Controller im selben Schema integrieren und eigene Models im vorgegebenen MobX‑Muster definieren. Der große Vorteil: Dein Projekt kann seine Domänenobjekte frei gestalten und nutzt Context, um diese zentral zu verwalten – inklusive Directus‑IO und Reaktivität – bis hin zur eigenen UI.

Muster: Controller mit initialize(...) #

Controller werden in der moduleConfig registriert und „lazy“ mit Abhängigkeiten verdrahtet. Das passt zu deinem bestehenden Pattern (siehe controllers/examination/ExaminationController.tsinitialize(examinationItemController, gameRelationController, gameId)).

Beispielcontroller (Schema):

// controllers/MyFeatureController.ts
import { makeObservable, observable, action, runInAction } from "mobx";
import { ItemController } from "context";

export default class MyFeatureController {
  _itemController!: ItemController<any>;
  _relationController?: any;
  _entityId?: number;

  isLoading = false;

  constructor() {
    makeObservable(this, {
      _itemController: observable,
      _relationController: observable,
      _entityId: observable,
      isLoading: observable,
      loadById: action,
      clear: action,
    });
  }

  // Wird von moduleConfig/Context aufgerufen
  initialize(
    itemController: ItemController<any>,
    relationController: any,
    entityId: number
  ) {
    this._itemController = itemController;
    this._relationController = relationController;
    this._entityId = entityId;
  }

  get store() {
    return this._itemController.store;
  }

  get selected() {
    return this._itemController.store.getSelected();
  }

  async loadById(id: number) {
    this.isLoading = true;
    try {
      await this._itemController.loadOne(id);
    } finally {
      runInAction(() => (this.isLoading = false));
    }
  }

  clear() {
    this._itemController.store.clearSelected();
  }
}
  • Analog zu deinem ExaminationController: Konstruktor setzt nur Basisstate; initialize(...) verdrahtet late‑bound Abhängigkeiten (ItemController, RelationController, IDs).

Muster: Model mit asJson() und updateFromJson(…) #

Models folgen einem festen Schema (vgl. models/items/CohortPatientModel.ts

models/items/GameClinicalDirective.ts):

  • asJson(): TItem – Serialisierung für Write‑IO (Directus).
  • updateFromJson(json: TItem): this – Mapping von API → Model.
  • Optional: getNewInstance() wenn dein Store es nutzt.
  • Reaktivität via MobX.

Beispielmodell (Struktur‑Vorlage):

// models/items/MyFeatureModel.ts
import { makeAutoObservable } from "mobx";
import { TItem } from "context";

export default class MyFeatureModel {
  id: number;
  title: string;
  relatedId?: number;

  constructor() {
    this.id = 0;
    this.title = "";
    this.relatedId = undefined;

    makeAutoObservable(this);
  }

  get asJson(): TItem {
    return {
      id: this.id,
      title: this.title,
      related_id: this.relatedId, // API-konforme Feldnamen
    };
  }

  updateFromJson(json: TItem) {
    this.id = json.id;
    this.title = json.title;
    this.relatedId = json.related_id;
    return this;
  }

  getNewInstance() {
    return new MyFeatureModel();
  }
}
  • Richte dich beim Mapping nach deinen Collections (z.B. patients_idgames_id in GameClinicalDirective, patients_id in CohortPatientModel).

Registrierung in moduleConfig #

Im Projekt werden Controller/Models in 

configs/moduleConfig.ts gemäß Schema zusammengeführt (siehe vorhandene Einträge wie GameControllerGameActivityController, etc.).

Beispiel (Ausschnitt):

// configs/moduleConfig.ts
import MyFeatureController from "@/controllers/MyFeatureController";
import MyFeatureModel from "@/models/items/MyFeatureModel";
import { ModuleConfig, ItemController } from "context";

export const myModuleConfig: ModuleConfig = {
  models: {
    myFeature: {
      model: MyFeatureModel,
      collection: "my_feature",
    },
  },
  controllers: (deps) => {
    const myFeatureItemController = new ItemController({
      model: MyFeatureModel,
      collection: "my_feature",
      client: deps.client, // Directus-Client aus Context
      notificationController: deps.notificationController,
    });

    const myFeature = new MyFeatureController();
    myFeature.initialize(
      myFeatureItemController,
      deps.someRelationController, // falls benötigt
      0
    );

    return {
      myFeatureController: myFeature,
    };
  },
};
  • Halte dich an das vorhandene Initialisierungsmuster deiner Controller (z.B. ExaminationController.initialize(...)).

Verwendung in der UI #

  • Provider ist bereits integriert (providers/ContextControllerProvider.tsx):
    • Baut new Controller(moduleConfig, routeConfig, routerFunctions).
    • Rendert Kinder erst, wenn controller.loaded true ist.
  • Zugriff in Screens/Komponenten:
import { observer } from "mobx-react";
import { useContextControllerProvider } from "context";
import { useEffect } from "react";

export default observer(function MyFeatureScreen() {
  const ctx = useContextControllerProvider();
  const myFeature = ctx.modules.myModule.controllers.myFeatureController;

  useEffect(() => {
    myFeature.loadById(1);
  }, []);

  if (myFeature.isLoading) return null;

  return <Text>{myFeature.selected?.title}</Text>;
});
  • Reaktivität: observer sorgt dafür, dass UI bei Änderungen im Store/Controller automatisch aktualisiert.

Kurzfazit #

Definiere eigene Models und (falls nötig) Controller. Registriere sie in der moduleConfig. Binde den Provider ein. Ab da übernimmt Context als Core deine zentrale App-Logik – inkl. Auth-Flow. Du fokussierst dich auf Domäne und UI. Plug & Play.

What are your feelings

Updated on Oktober 13, 2025