f10e15302d6c9dc7abb9848dccb1e750d8b51958
SystemLifecycleManagement.md
... | ... | @@ -0,0 +1,80 @@ |
1 | +# Managed Component System |
|
2 | + |
|
3 | +## Introduction |
|
4 | + |
|
5 | +A system consists of components working together. These components need to be |
|
6 | +initialized (started) and wired together, and when the system is torn down the |
|
7 | +individual components need to be torn down (stopped). The order in which they |
|
8 | +are started and stopped depends on the dependencies between the components. |
|
9 | + |
|
10 | +Components are often stateful, leading to specific concerns when doing |
|
11 | +[[Interactive Programming]] against a live process. One needs to resolve issues |
|
12 | +arising from stale state, and allow for dynamic reconfiguration and reloading |
|
13 | +when in the course of development the system structure or configuration changes. |
|
14 | + |
|
15 | +## Pattern Description |
|
16 | + |
|
17 | +In **System Lifecycle Management** there are **Components**, which have `start` |
|
18 | +and `stop` routines. The start routine accepts configuration and dependent |
|
19 | +components, the `stop` routine accepts the previously started component. These |
|
20 | +components are assembled and provided configuration values in a **System |
|
21 | +Description**. |
|
22 | + |
|
23 | +The **System Description** can then be started as a whole, which will start all |
|
24 | +components in topological order. |
|
25 | + |
|
26 | +Various extensions and variations exist, but these are the key ingredients. |
|
27 | + |
|
28 | +## Relationship to Dependency Injection |
|
29 | + |
|
30 | +While system lifecycle management in Clojure shares similarities with, and can |
|
31 | +be said to be a type of, dependency injection, it serves a more specialized |
|
32 | +purpose and boasts a unique history within the Clojure ecosystem. |
|
33 | + |
|
34 | +Much of this has to do with the dynamic nature of Clojure, and the cultural |
|
35 | +preference and excellent affordances for [[Interactive Programming]] (i.e. using |
|
36 | +a REPL). This means a system doesn't just need to be able to start up once, it |
|
37 | +also needs to be torn down cleanly, restart, receive new configuration while |
|
38 | +it's running, and start or stop incrementally (intentionally or because of |
|
39 | +failures of certain components to start). Different solutions in the Clojure |
|
40 | +space have differing degrees of support for these dynamic use cases. |
|
41 | + |
|
42 | +Frameworks like Java Spring use XML configuration to declare system and |
|
43 | +component structure and dependencies, by providing class names and construction |
|
44 | +hints. This is a highly static approach, which doesn't provide support for full |
|
45 | +lifecycle management, or facilitate dynamic reconfiguration. |
|
46 | + |
|
47 | +## Alessandra Sierra's Component library and Reloaded Workflow Article |
|
48 | + |
|
49 | +The cultural notion in the Clojure ecosystem of a "component" library and its |
|
50 | +purpose originated with Alessandra Sierra's |
|
51 | +[Component](https://github.com/stuartsierra/component) library, and the |
|
52 | +accompanying [Clojure Workflow Reloaded](https://cognitect.com/blog/2013/06/04/clojure-workflow-reloaded) |
|
53 | +article. |
|
54 | + |
|
55 | +It highlighted the main purpose of system lifecycle management: eliminating |
|
56 | +stale REPL state. |
|
57 | + |
|
58 | +> ... some aspects of Clojure's runtime are not quite as late-binding as one might |
|
59 | +> wish for interactive development. For example, the effect of a changed macro |
|
60 | +> definition will not be seen until code which uses the macro has been |
|
61 | +> recompiled. Changes to methods of a defrecord or deftype will not have any |
|
62 | +> effect on existing instances of that type. |
|
63 | + |
|
64 | +Part of the solution is found in |
|
65 | +[tools.namespace](https://github.com/clojure/tools.namespace), which allows |
|
66 | +reloading a set of namespaces in dependency order. However as Sierra points out |
|
67 | +tools.namespace alone is not enough. It is also imperative that the version of |
|
68 | +the application that's running, and any in-memory state it holds, matches with |
|
69 | +version of the application that is encoded in the source files. |
|
70 | + |
|
71 | +So the proposed approach is to combine tools.namespace with a "component" |
|
72 | +(system lifecycle) library. It shuts down the system, removing any stale state, |
|
73 | +then reloads all code, and then uses the newly loaded code to construct a new |
|
74 | +system state. |
|
75 | + |
|
76 | +## History |
|
77 | + |
|
78 | +### 2024-06-10 |
|
79 | + |
|
80 | +- First public version (Ariel Alexis, editing by Arne Brasseur) |