1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
| memory-model := persistence.
persistence |= memory-model.
ui =|= memory-model.
backend =|= memory-model.
```Visually represented as:

### Architectural Building Blocks
This approach heavily relies on several architectural elements. First are _stores_, inspired by the in-process REST style, functioning as in-process HTTP servers (without actual HTTP) or composable dictionaries. The core store protocol uses GET, PUT, and DELETE verbs as messages.
Polymorphic Identifiers replace URLs in this REST adaptation. These objects reference values within the store but aren't direct pointers, enabling them to reference objects not yet present.
Polymorphic Identifiers can be tailored to the application. For instance, they might consist solely of a numeric ID.
### MVC Revisited
The essence of MVC lies in separating input and output processing. In MVC, the view (or a controller) modifies the model, and processing halts. Later, the model notifies the UI of changes through a notification mechanism.
Smalltalk MVC utilizes a dependents list within the model, where interested views register. These views receive a `#changed` message upon model changes. Cocoa achieves this via `NSNotificationCenter`, but any broadcast mechanism suffices.
Views then update themselves by querying the model. Cocoa largely automates this for views: upon notification, the view invalidates itself, triggering an automatic redraw in the next event loop cycle.
This decoupling is crucial because update notifications can stem from various sources: user interactions, backend responses, or remote events.
This decoupled approach ensures uniform handling of all events, allowing the UI to focus solely on the local model. Consequently, the UI remains largely independent of network communication, resulting in a locally testable, local-first application.
This architecture refines MVC's view update mechanism by introducing a queue containing polymorphic identifiers of modified items. This queue further decouples the model and view. For example, it simplifies making the queue writable from any thread while ensuring it's emptied only onto the main thread for view updates. Additionally, update notifications become asynchronous – the updater adds an entry to the queue without waiting for UI updates.
This queue-based decoupling effectively prevents rapid model updates from overwhelming the UI or slowing down the model, addressing common performance bottlenecks.
The queue's polymorphic identifiers enable the UI to update at its own pace and discard irrelevant updates, such as duplicates for the same element.
Views can also utilize these identifiers to determine update necessity by matching against the identifier they are currently processing.
### Backend Communication Redefined
Many mobile applications implement REST backend communication using "convenient" cover methods for each endpoint operation, potentially auto-generated.
This approach disregards the fact that REST relies on a few verbs and numerous identifiers (URLs). This architecture employs a single backend communication channel: a queue accepting a polymorphic identifier and an HTTP verb. The identifier translates to a backend system URL, the request is executed, and the result is stored using the provided identifier.
Once stored, an MVC notification with the identifier is queued as described earlier.
This backend operation queue mirrors the model-view communication queue, including request deduplication to ensure only the final object version is sent. Remaining processing follows a pipes-and-filters architecture using polymorphic write streams.
For backend-initiated communication, URLs can be sent via sockets or other mechanisms, prompting the client to pull data using its regular request channels, maintaining the system's pull-based constraint.
This architecture makes backend requests explicit and reified, rather than implicitly encoded within the call stack. This provides the UI with clear visibility into communication failures common in mobile environments, enabling appropriate user feedback and preventing accidental duplicate requests.
Despite enhanced visibility and introspection, backend communication code is significantly reduced and isolated. Network code operates independently from the UI, and vice versa.
### Persistence Through Stacked Storage
Persistence is managed through stacked stores (storage combinators).

The application connects to the top of this stack, the CachingStore, which appears identical to the DictStore (an in-memory store). If a read request misses the cache, data is fetched from disk and converted from JSON by a mapping store.
For testing purposes, the in-memory store can replace the disk store, providing the same interface and behavior but with increased speed and no persistence.
Writes utilize the same asynchronous queues as other parts of the system. The writer retrieves object identifiers, fetches corresponding objects from the in-memory store, and then persists them. Sharing the same mechanism, writes benefit from the same deduplication, adapting to I/O overload by dropping redundant writes.

### Outcomes and Advantages
This reference architecture replaces complex code with a simpler, more concise alternative, promoting code reuse across the system. It also fosters independence between system components and optimizes performance.
Moreover, the combination of composable REST-like stores and constraint/event-based communication enables high decoupling, similar to well-implemented microservices architectures, but within mobile apps without relying on multiple processes (often restricted).



|