MV Whatever Hypertext as the engine of Application State - sgml/signature GitHub Wiki
HATEOAS
+-------------------------------+
| Initial GET request to API |
| e.g. GET https://api.example |
+-------------------------------+
|
v
+----------------------------------+
| Response contains _links object |
| e.g. |
| "_links": { |
| "self": { "href": "/resource" }|
| "next": { "href": "/page/2" } |
| "related": { "href": "/other" }|
| } |
+----------------------------------+
|
+---------------------+----------------------+
| | |
v v v
GET /resource GET /page/2 GET /other
| | |
v v v
More _links More _links More _links or content
to follow to follow to follow
MVC Using SQLite Schema, Python Event Loops, and SQLite Triggers
SQLite Schema
-- States
CREATE TABLE states (
name TEXT PRIMARY KEY,
type TEXT, -- 'normal' or 'final'
entry_action TEXT -- optional
);
-- Transitions
CREATE TABLE transitions (
source TEXT,
target TEXT,
condition TEXT, -- optional
FOREIGN KEY(source) REFERENCES states(name),
FOREIGN KEY(target) REFERENCES states(name)
);
-- Context
CREATE TABLE context (
key TEXT PRIMARY KEY,
value TEXT
);
-- Current state
CREATE TABLE current_state (
name TEXT PRIMARY KEY
);
SQLite Triggers
-- Trigger: On state change, run entry action
CREATE TRIGGER run_entry_action
AFTER UPDATE ON current_state
BEGIN
-- Clear input
UPDATE context SET value = '' WHERE key = 'inputValue'
AND (SELECT entry_action FROM states WHERE name = NEW.name) = 'clearInputValue';
-- Focus input
UPDATE context SET value = '1' WHERE key = 'isFocused'
AND (SELECT entry_action FROM states WHERE name = NEW.name) = 'focusInput';
END;
-- Trigger: Transition from checkInputState to clearValue if input is empty
CREATE TRIGGER transition_check_input
AFTER UPDATE ON current_state
WHEN NEW.name = 'checkInputState' AND
(SELECT value FROM context WHERE key = 'inputValue') = ''
BEGIN
UPDATE current_state SET name = 'clearValue';
END;
-- Trigger: Transition from clearValue to checkFocus
CREATE TRIGGER transition_clear_value
AFTER UPDATE ON current_state
WHEN NEW.name = 'clearValue'
BEGIN
UPDATE current_state SET name = 'checkFocus';
END;
-- Trigger: Transition from checkFocus to setFocus if not focused
CREATE TRIGGER transition_check_focus
AFTER UPDATE ON current_state
WHEN NEW.name = 'checkFocus' AND
(SELECT value FROM context WHERE key = 'isFocused') = '0'
BEGIN
UPDATE current_state SET name = 'setFocus';
END;
-- Trigger: Transition from setFocus to done
CREATE TRIGGER transition_set_focus
AFTER UPDATE ON current_state
WHEN NEW.name = 'setFocus'
BEGIN
UPDATE current_state SET name = 'done';
END;
Python Driver
import sqlite3
conn = sqlite3.connect(":memory:")
cursor = conn.cursor()
# Load schema and triggers
cursor.executescript(open("schema.sql").read())
cursor.executescript(open("triggers.sql").read())
# Insert states
cursor.executemany("INSERT INTO states VALUES (?, ?, ?)", [
("checkInputState", "normal", None),
("clearValue", "normal", "clearInputValue"),
("checkFocus", "normal", None),
("setFocus", "normal", "focusInput"),
("done", "final", None)
])
# Insert context
cursor.executemany("INSERT INTO context VALUES (?, ?)", [
("inputValue", ""), # Empty input triggers clearValue
("isFocused", "0") # Not focused triggers setFocus
])
# Set initial state
cursor.execute("INSERT INTO current_state VALUES ('checkInputState')")
conn.commit()
State Machine
def state_machine_gen(conn):
cursor = conn.cursor()
while True:
state = cursor.execute("SELECT name FROM current_state").fetchone()[0]
yield state
# Final state check
state_type = cursor.execute("SELECT type FROM states WHERE name = ?", (state,)).fetchone()[0]
if state_type == "final":
return
# Trigger next transition by re-setting current state
cursor.execute("UPDATE current_state SET name = ?", (state,))
conn.commit()
Example
gen = state_machine_gen(conn)
try:
while True:
state = next(gen)
print(f"π’ Transitioned to: {state}")
except StopIteration:
print("β
State machine completed.")
Output
π’ Transitioned to: checkInputState
π’ Transitioned to: clearValue
π’ Transitioned to: checkFocus
π’ Transitioned to: setFocus
π’ Transitioned to: done
β
State machine completed.
Robustness
Category | Error Eliminated | How It's Prevented |
---|---|---|
Control Flow | Infinite loops / runaway recursion | No loops used; transitions are bounded by declarative triggers |
State Drift | Invalid or skipped states | Transitions are enforced via foreign keys and trigger logic |
Race Conditions | Concurrent mutation of state/context | SQLite's transactional integrity ensures atomic updates |
Guard Evaluation | Misapplied or skipped guard conditions | Guards encoded in SQL WHEN clauses, not procedural logic |
Entry Action Execution | Forgotten or duplicated entry actions | Triggers fire automatically on state change |
Transition Ordering | Out-of-order or ambiguous transitions | Deterministic trigger firing order via SQLite engine |
Context Mutation | Inconsistent context updates | Context is mutated via SQL triggers with scoped conditions |
Final State Handling | Failure to detect or halt on final state | Final state type checked explicitly before yielding |
Auditability | Missing transition logs or state history | Can be extended with append-only log tables and triggers |
Reproducibility | Non-deterministic execution paths | Entire flow encoded in schema and metadata; replayable via SQL |
Dependency Injection | Implicit dependencies in procedural code | All dependencies (guards/actions) are explicit in schema |
Loop Exit Conditions | Incorrect or missing loop termination | No loops used; generator yields and triggers drive progression |
State Machine Diagram
ββββββββββββββββββββββββββββββ
β context table β
ββββββββββββββββββββββββββββββ
β key | value β
ββββββββββββββββΌββββββββββββββ
β inputValue | "..." β
β isFocused | "0" or "1" β
ββββββββ¬βββββββββ΄βββββββββββββ
β
β (read/write via triggers)
βΌ
ββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββ
β current_state ββββΆβ states table β
ββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββ
β name | active β β name | entry_action β
ββββββββββββββββΌββββββββββββββ βββββββββββββββΌβββββββββββββββ
β "checkInput" | β β β "clearValue"| "clearInput" β
ββββββββ¬βββββββββ΄βββββββββββββ ββββββββ¬βββββββββ΄βββββββββββββ
β β
β UPDATE β JOIN
βΌ βΌ
ββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββ
β transitions table β β triggers (SQL) β
ββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββ
β source | target β β ON UPDATE current_state β
β condition | β β WHEN context matches guard β
βββββββββββββββΌβββββββββββββββ β β UPDATE current_state β
β "checkInput"| "clearValue" β β β UPDATE context β
β | β β β INSERT INTO audit_log ββββββ
βββββββββββββββ΄βββββββββββββββ ββββββββββββββββββββββββββββββ β
β² β² β
β β β
β ββββββββββββββββββββ΄βββββββββββββββββββββ β
β β audit_log table (optional) ββββββββββββββββ
β βββββββββββββββββββββββββββββββββββββββββ
β β timestamp | from_state | to_state β
β β context_snapshot | transition_id β
β βββββββββββββββββββββββββββββββββββββββββ
β
β
βΌ
ββββββββββββββββββββββββββββββ
β Python generator β
ββββββββββββββββββββββββββββββ
β yield current_state β
β next() β triggers β
ββββββββββββββββββββββββββββββ
β²
β
β
βββββββββββ΄ββββββββββ
β deterministic βββββ Declarative logic ensures
β transitions β reproducible outcomes
βββββββββββββββββββββ
MVC
- https://plainenglish.io/blog/flask-crud-application-using-mvc-architecture
- https://medium.com/shecodeafrica/understanding-the-mvc-pattern-in-django-edda05b9f43f
- https://medium.com/nerd-for-tech/introduction-to-ruby-on-rails-and-how-mvc-works-c56dff61dce5
- https://dev.to/vikbert/use-d-mvc-pattern-in-symfony-application-5f90
- https://www.tutorialspoint.com/symfony/symfony_complete_working_example.htm
- https://github.com/openfl/openfl-samples/blob/master/script.hx
MVVM
- https://namitamalik.github.io/MVC-and-MVVM-with-AngularJS/
- https://subscription.packtpub.com/book/web-development/9781786469946/2/ch02lvl1sec18/mvvm-architectural-pattern
- https://moduscreate.com/blog/ext-js-to-react-handling-application-state-with-mobx/
- https://medium.com/react-weekly/building-a-react-mobx-application-with-mvvm-ec0b3e3c8786
- https://legacy.gitbook.com/book/developmentarc/react-indepth/discussions/18
- https://medium.com/react-native-training/ditching-setstate-for-mobx-766c165e4578
- https://medium.com/react-weekly/building-a-react-mobx-application-with-mvvm-ec0b3e3c8786
- https://medium.cobeisfresh.com/level-up-your-react-architecture-with-mvvm-a471979e3f21
- https://swizec.com/blog/awkward-thing-mobx-complex-models/swizec/7260
- https://mobx.js.org/refguide/autorun.html
- https://github.com/mobxjs/mobx/issues/300
- https://github.com/mobxjs/mobx-react-todomvc
- https://github.com/ryanatkn/react-mobx-typescript-experiments
- https://libraries.io/github/mobxjs/mobx-react
- https://codepen.io/evgen/pen/NgpVMw
Constructors vs Getters and Setters
Why should I create a method just to hold a one line function? (+Constructor)
How painful is it going to be to refactor two, three, four, five or more getters/setters vs one constructor?(+Constructor)
How hard is it going to be to document two, three, four, five or more getters/setters vs one constructor?(+Constructor)
Is there going to be a default value which will be documented? (+Constructor)
Do I like documentation and expect people to read? (+Constructor)
Will the initial value be undefined?(+Setter)
Is there a set of equivalent forms (shorthand, international, nicknames) which will all be acceptable as syntatically correct for required arguments? (+Setter)
Is there a set of optional arguments with default values? (+Setter)
Is there a common need to stringify and parse the initial value? (+Setter)
Do I dislike documentation and expect people to experiment? (+Setter)