{"id":23974647,"url":"https://github.com/hal/circuit","last_synced_at":"2025-10-09T02:41:54.186Z","repository":{"id":17894079,"uuid":"20844705","full_name":"hal/circuit","owner":"hal","description":"Uni-directional data flow model for GUI applications.","archived":false,"fork":false,"pushed_at":"2016-06-27T22:17:43.000Z","size":1000,"stargazers_count":21,"open_issues_count":3,"forks_count":7,"subscribers_count":4,"default_branch":"develop","last_synced_at":"2025-04-13T22:45:19.711Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hal.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-06-15T00:13:36.000Z","updated_at":"2021-12-05T15:48:40.000Z","dependencies_parsed_at":"2022-09-24T03:02:25.049Z","dependency_job_id":null,"html_url":"https://github.com/hal/circuit","commit_stats":null,"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/hal/circuit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hal%2Fcircuit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hal%2Fcircuit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hal%2Fcircuit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hal%2Fcircuit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hal","download_url":"https://codeload.github.com/hal/circuit/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hal%2Fcircuit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279000773,"owners_count":26082906,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-09T02:00:07.460Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-07T05:48:00.746Z","updated_at":"2025-10-09T02:41:54.156Z","avatar_url":"https://github.com/hal.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Circuit\n\nCircuit provides an unidirectional data flow model for GUI applications. It's intended to be used with GWT, but can be leveraged in any Java GUI framework. \n\nIt resembles the ideas of the [Flux Architecture](http://facebook.github.io/react/docs/flux-overview.html) that can be found in the [React.js](http://facebook.github.io/react/index.html) framework, but adds certain semantics to the data flow model.\n\nFor a general introduction and problem statement we'd recommend to look at the Flux documentation first. The specifics of the Circuit implementation will be explained in the following sections.\n\n## Core Components\n\n### Data Flow Model\n\nView components (presenters in this case) dispatch actions through central dispatcher. The dispatcher coordinates the processing of actions across stores. Stores process the data or state change associated with an action and emit change events once they are done. Interested parties (presenters) read the data of the store and update the views accordingly.\n\n```\n                                             Process(Action)\n                      +-----------------+\n                      |   Dispatcher    | +--------------+\n                      |                 |                |\n                      +-----------------+                |\n                                                         |\n                             ^                           v\n                       Action|\n+-----------+                |                   +----------+\n|   View    |  Interaction   |       read        |  Store   |\n|           | +----------+   |   +-------------\u003e |          |\n+-----------+            v   |   |               +----------+\n                             |   |\n    ^                 +------+---+------+                +\n    |                 |    Presenter    |                |\n    +---------------+ |                 |  \u003c-------------+\n           update     +-----------------+           Change Event\n\n                           ^\n                           |  Lifecycle\n                       +---+------------+\n                       |   Framework    |\n                       |                |\n                       +----------------+\n\n```\n\n### Actions\nActions represent behaviour, data and state within an application. They signal state changes to the dispatcher, which in turn coordinates the processing of actions across stores.\n\nActions are most often initiated from user interaction, but they are not limited to that. It's also possible that the underlying framework or the service backend creates and dispatches actions.\n\n```java\n@ActionType\npublic class SaveTodo implements org.jboss.gwt.circuit.Action {\n \tprivate final Todo todo;\n\t[...]\n\tpublic Todo getTodo() { return todo; }\n}\n```\n\n### Dispatcher\nThe dispatcher acts as a central hub for processing actions. Any action passes through the dispatcher and the dispatcher delegates it to the Stores, that do ultimately process the Action.\n\nThe Dispatchers main responsibility is to coordinate the processing of Actions across Stores. \n\n```java\npublic interface Dispatcher {\n\n    \u003cS\u003e void register(Class\u003cS\u003e store, StoreCallback callback);\n\n    void dispatch(Action action);\n}\n```\n\n### Stores\nStores keep the application state and act as proxies to the data model used by an application. Most often stores interact with service backends to read and modify a persistent data model, which they in turn expose in a read-only fashion to the actual view (or presenter-view tuples in MVP).\n\nStores are registered with the Dispatcher for Actions they are interested in. They can directly rely on the data passed with an Action, or listen for state changes in other parts of the data model.\n\n```java\npublic class TodoStore {\n\n\tprivate Todo selectedTodo;\n\tprivate final List\u003cTodo\u003e todos;\n\n\t@Process(actionType = SaveTodo.class)\n\tpublic void onSave(final SaveTodo todo, final Dispatcher.Channel channel) {\n\t\t// persist the todo (backend call)\n    }\n}\n```\n\n### Presenter (as in MVP)\n\nThe Presenter (or presenter-view tuple as in MVP) creates and dispatches Actions. This happens on behalf of a user interaction, due to framework events or another signal from the service backend. \n\nPresenters listen to Store Change Events and in turn read data from Stores and update the views accordingly.\n\nPresenters do only have read-only access to Stores and the data they maintain. Any modification to the data or state of an application has to be driven by Actions. \n\n```java\nclass TodoPresenter() {\n\t\n\tTodoStore todoStore;\n\n\tpublic TodoPresenter() {\n\t\ttodoStore.addChangeHandler(\n\t\t    new PropagatesChange.Handler() {\n  \t\t\t\t@Override\n    \t\t\tpublic void onChanged(Class\u003c?\u003e actionType) {\n\t\t\t\t\t// when the model changed, update the view\n    \t\t\t\tupdateView(todoStore.getTodos());                     \n \t\t\t\t}\n  \t\t});\n\t}\n}\n```\n\n## Processing Semantics\n\nOne of the core problems Circuit addresses are cascading effects of event based applications. \n\nIn a typical GUI application an event triggers some business logic, model update or state change, most often as a result of user interaction. Events can trigger other events, which leads to unpredictable data flow, hard to diagnose problems and unclear application semantics.\n\nThe guiding principal in Circuit (and Flux) is to provide a framework with deterministic behaviour that allows you to hook into the data flow at any point and know exactly what steps will be executed next.\n\nThe uni-directional data flow described above already provides a good baseline, but Circuit adds some specific semantics to the contract between the core components, which will be described in the next sections.\n\n### Action Sequences\n\nAny Action flows through the Dispatcher and the dispatcher coordinates how the Stores process the actions. In Circuit we use a sequencing dispatcher, that ensures only one Action will be processed at a time. \n\nIf two Actions are dispatched simultaneously the later one will be queued. All Stores that are registered for a particular Action type will process the Action and once the group completes the next Action will be taken of the queue.\n\nThis way Actions don't create race conditions when updating the state or data of an application.\n\n### Store Interdependencies\n\nTypically a single Store maintains a particular segment of the data or domain model in an application and the relevant state associated with it. On most applications Stores don't exist in isolation, but depend on other model parts to perform their work.\n\nCircuit allows you to express dependencies between Stores on the level of an Action type. \n\n```java\npublic class TodoStore {\n    @Process(actionType = RemoveUser.class, dependencies = {UserStore.class})\n    public void onRemoveUser(RemoveUser user, final Dispatcher.Channel channel) {\n\t\t// when a user is removed we removes his todo's \n\t\t[...]\n    }\n}\n```\n\n### Contract Between Store and Dispatcher\n\nStores are registered with a `StoreCallback` against the Dispatcher. This callback is the contract between the store and the dispatcher and consists of three phases: \n\n1. Prepare Action\n1. Complete Action\n1. Signal Change Event \n\nDuring the preparation phase Stores signal interest in a particular Action type and any dependencies they have on other Stores for a particular Action type. \n\nThe Dispatcher creates a dependency graph for each action type and invoke the Stores in an ordered way, one at a time.\n\nThis way Stores can safely rely on the State of other Stores during the processing of an Action.\n\nUpon completion Stores consume the Action and acknowledge successful processing. Finally Change Events are fired to signal interested parties that the data or state of the application has changed. Since Stores process the Action in an ordered way, the Change Events follow that pattern.\n\n#### Action Acknowledgement\n\nMany Store implementations rely on asynchronous invocations to the service backend. Circuit was build to provide support for asynchronous flow control in Stores.\n\nWhen Stores complete the processing of an Action, they acknowledge the Action they processed. This signals the Dispatcher that the next Store can start processing the Action.\n\n```java\npublic class TodoStore {\n\n\t@Process(actionType = SaveTodo.class)\n\tpublic void onSave(final SaveTodo todo, final Dispatcher.Channel channel) {\n\n\t\t// async invocation\n    \ttodoService.save(todo.getTodo(), new TodoCallback\u003cVoid\u003e(channel) {\n            @Override\n            public void onSuccess(final Void result) {\n\t\t\t\t// acknowledgement\n\t\t\t\tchannel.ack();\n            }\n        });\n    }\n}\n```\n\n#### Change Events\n\nWhen an Action is acknowledged by a Store, the Dispatcher will fire a Change Event for that Store. However, this only happens *after* the Action was processed by all Stores. The order of Change Events follows the order in which the Action was processed by the Stores.\n\n## Programming Model\n\nCircuit provides a minimalistic API with a handful of interfaces and some reasonable default implementations. When it comes to implementation, Circuit tries to be as less intrusive as possible. The following section describes the API for the basic building blocks.\n\n### API\n\n#### Action\n\nAn action is defined by a class which implements the marker interface `org.jboss.gwt.circuit.Action`. In many cases actions are simple POJOs which should implement a reasonable `equals()` and `hashCode()` method. An action can carry a payload which is used by the stores, however there might be cases where an action is more like a command without a payload at all.\n\n#### Store\n\nStores do not need to implement a special interface. However they need to register with the Dispatcher for actions they are interested in. This registration is done by implementing the interface `org.jboss.gwt.circuit.StoreCallback`. The methods on this interface reflects the two phases (prepare and complete) as described above:\n\n- `Agreement voteFor(Action action)` to express support for an action and optional dependencies to other stores and \n- `void complete(Action action, Dispatcher.Channel channel)` which is called when an action is dispatched to the store.\n\n#### Dispatcher\n\nThe dispatcher interface mainly consists of two methods:\n\n- `\u003cS\u003e void register(Class\u003cS\u003e store, StoreCallback callback)` to register a callback for a given store and\n- `void dispatch(Action action)` to dispatch actions\n\nWhile you are free to implement your own dispatcher, Circuit comes with a default implementation: `org.jboss.gwt.circuit.dag.DAGDispatcher`. The `DAGDispatcher` provides sequential action processing and takes care of inter-store dependencies as described above. \n\nTo see an example on how to use the API in more detail, take a look at the [calculator](samples/calculator) example. \n\n### Annotation Based Approach\n\nAs an alternative to using the API directly and implementing all the bits and pieces manually, Circuit comes with a set of annotations and an APT processor which generates most of the boilerplate code for you. The annotations step in when it comes to write the code for the callback registration and declare store dependencies. \n\nThe entrance ticket is the `@Store` annotation which must be placed on the store implementation:\n\n```java\n@Store\npublic class ShoesStore {\n    [...]\n}\n```\n\nA store implementation marked with `@Store` must contain one or more methods marked with `@Process`. This annotation tells Circuit what action type the method can process. Furthermore dependencies to other stores can be expressed using `@Process`:\n\n```java\n@Store\npublic class ShoesStore {\n\n    @Process(actionType = Dress.class, dependencies = {TrousersStore.class, SocksStore.class})\n    public void dress(Dispatcher.Channel channel) {\n        [...]\n        channel.ack();\n    }\n}\n```\n\nThe signature for methods annotated with `@Process` must adhere the following rules:\n\n- The return type must be `void`\n- The last parameter (or the only one, if no action is referenced) must be of type `org.jboss.gwt.circuit.Dispatcher.Channel`.\n- The first parameter can be the action which is processed.\n\nTo see the annotations in action take a look at the [wardrobe](samples/wardrobe) and [todo](samples/todo) samples.\n \n#### What's Generated?\n\nFor each class annotated with `@Store` a store adapter will be generated. The adapter takes the store adaptee and the dispatcher as constructor parameters and will take care of registering the store callback against the dispatcher . Thus the adapter satisfies the two phase contract (vote and complete). The following code snippet shows the generated store adapter for `ShoesStore`:\n\n```java\n/*\n * WARNING! This class is generated. Do not modify.\n */\n@ApplicationScoped\n@Generated(\"org.jboss.gwt.circuit.processor.StoreProcessor\")\npublic class ShoesStoreAdapter {\n\n    @Inject\n    public ShoesStoreAdapter(final ShoesStore delegate, final Dispatcher dispatcher) {\n        dispatcher.register(ShoesStore.class, new StoreCallback() {\n            @Override\n            public Agreement voteFor(final Action action) {\n                Agreement agreement = Agreement.NONE;\n                if (action instanceof Dress) {\n                    agreement = new Agreement(true, TrousersStore.class, SocksStore.class);\n                }\n                [...]\n                return agreement;\n            }\n\n            @Override\n            public void complete(final Action action, final Dispatcher.Channel channel) {\n                if (action instanceof Dress) {\n                    delegate.dress(channel);\n                }\n                [...]\n            }\n            \n            @Override\n            public void signalChange(final Action action) {\n                [...]\n            }            \n        });\n    }\n}\n```\n\n#### Goodies\n\nWhen using the annotation based approach, Circuit checks as an additional benefit whether you've defined circular dependencies. Circular dependencies occur when two stores depend on each other for the same action type:\n\n```java\n@Store \npublic class StoreA {\n\n    @Process(actionType = FooAction.class, dependencies = StoreB.class)\n    pubic void andAction(Dispatcher.Channel channel) {\n        [...]\n    }\n}\n\n@Store \npublic class StoreB {\n\n    @Process(actionType = FooAction.class, dependencies = StoreA.class)\n    pubic void andAction(Dispatcher.Channel channel) {\n        [...]\n    }\n}\n```\n\nFor this setup Circular would issue an error during the code generation phase. \n\nFurthermore Circular generates a [GraphViz](http://www.graphviz.org/) graph which shows the dependencies between the stores for each action type. Below you can see the graph for the [wardrobe](samples/wardrobe) example:\n\n![Dependencies](doc/dependencies.png)\n\nThe arrows read as \"depends on\". That is for the `Dress` action the `ShoesStore` depends on the `TrousersStore` which in turn depends on the `UnderwearStore`. Or to put it in other words: You have to put on the underwear before you can put on the trousers; and you have to put on the trousers before you can put on the shoes.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhal%2Fcircuit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhal%2Fcircuit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhal%2Fcircuit/lists"}