frame-lang / frame_transpiler

Frame is a markdown language for creating state machines (automata) in Python as well as generating UML documentation.
MIT License
65 stars 9 forks source link

The Frame System Design Language (FSDL) and Runtime #71

Open frame-lang opened 2 years ago

frame-lang commented 2 years ago

Draft - In Progress

Purpose

To present a high-level proposal for a wholistic software system design language and runtime environment for deploying controller logic and infrastructure to a diverse set of target environments (front-end, cloud, embedded, etc.) architected in accordance with the actor model.

Background

Classical systems engineering organizes design artifacts into two broad types - structural and behavioral. Structural artifacts describe what a system is made of while behavioral artifacts define what a system does.

Currently tools such as Terraform and others can define the structural aspects of software infrastructure but lack the ability to specify software system behavior. In contrast, the Frame language describes system behavior but has no concept of system structure or how to manipulate it.

Additionally, Frame notation is currently limited to describing the behavior of a single "controller", which may or may not be the complete behavioral logic of even one system. Often (perhaps usually) interesting software systems are best described as a system of systems that cooperate to achieve some larger purpose (such as flying an airplane). FSDL will expand Frame notation to address this additional scope in its original mission.

By including other declarative languages as first class aspects of the technology as well as expanding the capabilties of Frame notation itself, FSDL seeks to define unified and universal software system design language.

FSDL

FSDL will define standardized software architecture components, specification languages and toolchains for building systems of systems. FSDL declarative, polyglot specification language includes both Frame notation for defining system behavior in conjunction with popular declarative languages for defining system structure.

System Controller Package (SCP)

The System Controller Package (SCP) is a bundle of resources and code containing a SysOS deployment for a target environment. The contents of the bundles are environment specific, but must minimally contain the SysOS runtime for the target environment and the Main Controller (MaC).

SCP Components

  1. System Operating System (SOS) runtime
  2. A Main Controller (MaC).
  3. The Effector
  4. Machine Operating Machine (MOM)
  5. System Controllers (SC) (optional).
  6. Message specifications.
  7. Channels, both internal and external to the system.
  8. A persistence mechanism for system state (optional).
  9. A distributed lock mechanism (optional).

System Operating System (SOS)

System Operating System (SOS) is the runtime environment enabling SCP components to operate in a target environment.

Main Controller

The Main Controller (MaC) is the first instantiated controller and will bootstrap the environment based on configuration data. It manages the lifecycle of all other controllers as well as establishes and/or verifies the system infrastructure.

The first responsibility of the MaC upon bootup is to determine if system the state needs to be retrieved from persistent storage. If so, it loads the system's persisted state and initializes it. Otherwise the system is booted into a default state.

The order of instantiation may be important. If it is critical that all controllers are operational before the system as a whole can perform certain operations, the MaC will need to be aware of such requirements and progressively instantiate the communication infrastructure as appropriate.

Alternatively, if it is permitted for the system to begin operations in a non-deterministic manner, then the order of instantiation and operational readiness of both the controllers and communications infrastructure can be ignored.

Effector

The Effector is an object that receives commands from the controllers and performs operations. It should not make contextual decisions and simply performs the requested operation and return data and results to the controller.

The Effector may be a composite of objects rather than a monolith, an approach which makes the runtime optimization of the system more flexible and optimizable.

Machine Operating Machine (MOM)

In most environments, controllers will not be first class objects. Therefore some management logic must exist to create, initialize and destroy the controller as well as provision it to communicate with the rest of the system.

MOM will be an environment specific runtime that handles these duties and includes the main entry point or equivalent for the environment.

System Controllers (SC)

System Controllers (SC), or just controllers, are Frame generated state machines that manage an external domain or simply serve as a mechanism for internal state or data management.

Messages

Messages should be specified using Protocol Buffers. This will enable easy support of both gRPC and REST protocols.

Channels

Channels are an abstraction of communication paths both internally and externally. Ideally they should be usable via standardized, simple Frame notation for sending and receiving messages. Channels should be definable using a standard interface API as well as with declarative configuration languages like Terraform.

Persistant Storage

SysOS will define an interface for persisting system state that can support a wide variety of standard data storage technologies (SQL, NoSQL, filesystem).

frame-lang commented 2 years ago

@walkie Interested in your thoughts on this basic concept. I am aiming to implement reference implementations in a few languages and target environments to validate the idea. Thoughts? Any obvious problems with the idea?

walkie commented 2 years ago

To be honest, I'm a bit out of my depth here! The domain seems potentially huge, which makes me a bit wary. However, seeing a reference implementation might help to clarify things.

One question about channels: Do these subsume the existing actions and event handlers? If not, should they? That is, should there be a common way of defining and interacting with these various forms of message passing?

Currently, actions are exclusively Frame->Host and events are primarily Host->Frame. However, there's a need for Frame->Frame (i.e. something like subroutines, which can be simulated with events but have some limitations and pollute the external interface), and ideally Frame->other-Frame (i.e. communication between state machines, which currently requires some glue code in the interface).

If the "channel" metaphor supported all of the possible combinations, perhaps with syntactic sugar for the common cases currently supported by actions and events, that could be nice from a language design and implementation perspective.

frame-lang commented 2 years ago

To be honest so am I :).

And yes the goal is to identify patterns/interfaces/symbology that works for any-to-any communication. The hope is that there can be a standardized approach to all the typical communications objects/protocols (http, sockets (tcp, udp), pipes, rpc, internal pub/sub libraries etc).

I don't believe this changes anything about event handlers and actions as they exist now. However new concepts/objects/notation/patterns would exist that would expand what was in the Frame universe. Some of these would be declared/referenced from a Frame spec. Others such as the SOS is just part of the runtime environment.

In general I'm hoping controllers can be removed from actually "doing" anything. Instead, they make decisions about events that come in and send out events about their decisions. I'm lumping all executive functions (calling APIs, changing global state etc) into an "Executor" object. The only difference here is that the controller doesn't make the low level call. Instead it decides what should be done and sends a message to the Executor which does the environment specific dirty work. This segregation between "decider" and "doer" code would make this approach completely generic and (theoretically) portable.

In terms of usage of a channel, I think conceptually at a high level it is pretty simple - a) polling or b) notifications to read data and write is write which succeeds or doesn't. However each of those communication objects works differently under the hood. Differences off the top of my head include buffering, directionality, sync/async and I'm sure many more.

I'm hoping that the FrameChannel object would paper over all of that and make them all work in a consistent way. It may not really matter in the end but it would be nice if possible.

As for notation I'm kind of in love with the <@> token to mean "Frame Channel". So creating a channel in Frame would look like this:

var global_channel:<@> = <@>(config_data)

I'm thinking that <@>(config_data) transpiles to a FrameChannel class/type that has a factory function that takes the config data and returns a standardized FrameChannel interface that holds a reference to the concrete comm mechanism object requested in the config data.

With regard to I/O, if the read config specifies to be notified about (rather than polling for) incoming messages then part of the config info must be what event to send to the controller.

Alternatively, if notification was desired but just as a pass through then perhaps a general purpose interface method on the controller might be interesting. I'm tentatively thinking dispatch(FrameEvent) as the standard interface method, but TBD. The FrameChannel would need to convert the wire data into a FrameEvent (rather than the controller interface) and directly "inject" the FrameEvent into the controller. I think this would be straightforward for RPC but would need some standardization for HTTP and other protocols.