Walkthrough

The three layers, and what a change costs.

A Yuneta system is compiled at the bottom and data at the top. Change the top and count the rebuilds.

Below is one system, drawn as the three layers it is made of. The bottom band is compiled: its gclasses are registered at start-up and its shape lives in main.c. The middle band is a treedb, and it changes while the process runs. The top band is what an end user sees.

The top band and the middle band render the same array of nodes. The read as switch changes the rendering, not the data. That is the claim this page exists to make: the third layer is not a third store.

SERVICES AND ROLES — data what the end user sees
read as change it
INFORMATION — treedb on timeranger2 CRUDLU, while the yuno runs
BEHAVIOR — the tree of gobjs fixed at build time
yuno A
C_TREEDB C_NODE
address = pointer
═══► ievent address = "__input_side__"
yuno B
__input_side__ C_AUTHZ
a named service
deliver destination
0structure changes
0rebuilds
0restarts
unchangedcompiled layer

One mechanism, two scopes

Yuneta has one way for objects to talk, and it is the message. What changes when a message leaves the process is not the mechanism. It is the address. Inside a yuno a gobj addresses another by pointer. Between yunos the same events travel as ievents, and the address is a name that c_ievent_srv.c resolves with gobj_find_service().

One rule follows, and the switch above breaks it on purpose: only a named service can hold either end of an inter-yuno conversation. A pointer does not travel over a websocket. A name does.

TreeDB is the structure, not the channel

A frequent first reading is that treedb is how yunos talk at the upper level. It is not. TreeDB holds what exists and how it relates. Messages stay the only channel — which is why every change you made above came back down as an EV_TREEDB_NODE_* event rather than being polled for. The producer publishes. The consumer subscribes.

A class is a role

A gclass header exposes two things: GOBJ_DECLARE_GCLASS(C_FOO) and register_c_foo(). It publishes no structure, and priv is opaque. A class with no visible structure keeps one identity only: what it does for the other objects. That identity is a role — which is why the word survives the trip from a treedb node written by an operator to a gclass registered by a developer in C.

Read the rule backwards to test a design. A gclass you cannot name by its role has none: it is a bag of code, or two roles fused. Names from the tree, graded:

NameVerdictWhy
C_PROT_HTTP_CL · C_TCP_S · C_PROT_MODBUS_M says it The suffix carries the role. A protocol name alone does not say client, server or master.
C_TCP silent The role is there by default — it is the client — and default is exactly what a reader cannot see. Compare its sibling C_TCP_S.
C_IDP_KEYCLOAK · C_DBA_POSTGRES correct shape Role first, implementation second. Preferring the domain word to the vendor name does not ban the vendor. It subordinates it.
C_TIMER0 · C_RESOURCE2 no role The 0 and the 2 name an implementation and a version. Nothing says how the part played differs, and C_TIMER0 is a repeated source of mistakes for that reason.
C_PEPON · C_TESTON no role They say nothing at all.

The rule has a limit, and the limit is useful. A gclass names the generic role: the part the class knows how to play anywhere. A gobj_name names the situational role: the part this instance plays in this tree. Push the situational role into the class name and you get one gclass for each place it is used.

Why the two layers stay apart

The compiled layer states what the software can do. It is a capability, and it is identical in every installation of the same binary. The dynamic layer states what this installation does, and for whom. It is deployment and authorization, and it differs for each customer, each realm and each day.

Merge the two and every new customer needs a new build. Keep them apart and one binary serves every installation, because the part that varies is data. The counter above is the whole argument in one number.

Which is also the discipline the layer demands: a declarative JSON structure is sugar over a runtime API, and that API must stay available while the yuno runs. When a feature must change a structure the configuration describes and no API exists, write the API. A layer you can change only at start-up is a build step with a different file format.

Where this is written down