In this post, we introduce the concept of Container in the Substrates API. A Container is a collection of Conduits of the same emittance data type. With the concept of Container, we introduce a new element in the Subject hierarchy. So instead of a Conduit being parented by a Circuit, it can now also be parented by a Container which is then parented by a Circuit.
Before Containers, we mapped an active processing or situational concept in a domain to a Circuit, where a Conduit would represent an entity concept and a Channel (and Percept) some behavior or mutable state of the entity.
With the Container interface, we can delegate management of a collection of Conduits to the Substrates runtime as opposed to the clients of Substrates as in our last post where we employed a map to track Conduits representing parties.
Here is the definition of a Container in the Substrates API.
1 2 3 4 5 6 7 |
@Provided interface Container < P, E > extends Component < Source < E > >, Pool < Pool < P > > { } |
A Container is a Pool of Conduits which are Pools for percepts.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/// Creates percepts that emit captured data to pipes. /// /// @param <P> the class type of the percept /// @param <E> the class type of emitted value @Provided interface Conduit < P, E > extends Component < E >, Pool < P >, Tap < Conduit < P, E > > { } |
The Container doesn’t expose the Conduits themselves, but instead, their Pools, which allows for implementation optimizations and ensures the Container has complete management of the Resources it provisions and manages.
The Container interface extends the Component interface, which extends the Context interface. This means a Container is a Source, but not a Source of the emittances of the underlying Conduits but the creation of Conduit’s Source instance.
We can then use the subscribe method in the Source interface to subscribe to each of the Conduits. We subscribe to the Container’s Source, and upon receiving a callback that provides access to a Conduit’s Source, we subscribe to the Channel.
An example will help cement our understanding of the Container and its usage. Let’s tackle the problem of capturing orders coming into an exchange where there are multiple stocks available to buy and sell.
1 2 3 4 5 6 7 8 9 10 |
final var CORTEX = cortex (); final var EXCHANGE = CORTEX.name ( "Exchange" ); final var ORDERS = CORTEX.name ( "Orders" ); final var BUY = CORTEX.name ( "Buy" ); final var SELL = CORTEX.name ( "Sell" ); record Order( int price, int quantity ) { } |
We’ll first create a Circuit. Then, we’ll create a Container that creates on-demand Conduits for each stock lookup.
1 2 3 4 5 6 7 8 |
var circuit = CORTEX.circuit ( EXCHANGE ); var container = circuit.container ( ORDERS, pipe ( Order.class ) ); |
Now we’ll subscribe to the Container’s Source. The stock is the Subject of the Conduit, and the type is the Subject of the Channel. The variable named orders is the Source of the Conduit, and the order variable is the emitted value.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
container.source ().subscribe ( stock -> orders -> orders.subscribe ( type -> order -> out.printf ( "stock: %s, type: %s, price: %d, quantity: %d%n", stock.name (), type.name (), order.price, order.quantity ) ) ); |
We now look up a stock named Pipe and emit an Order. The first get call below gives us access to the Pool for a stock-mapped Conduit and the second get call gives us access to the order’s type Pipe.
1 2 3 4 5 6 7 8 9 10 11 |
container .get ( CORTEX.name ( "APPL" ) ) .get ( BUY ) .emit ( new Order ( 200, 1000_000 ) ); container .get ( CORTEX.name ( "GOOG" ) ) .get ( SELL ) .emit ( new Order ( 180, 100_000 ) ); |
The following will be printed by our subscriber above.
1 2 3 4 |
stock: APPL, type: Buy, price: 200, quantity: 1000000 stock: GOOG, type: Sell, price: 180, quantity: 100000 |
If we were to print out the Subject of an order type it would be like this:
1 2 3 4 5 6 |
Subject[name=Exchange,type=CIRCUIT,id=a182a64e-36df-44ad-a7a6-4305aa551a1f] Subject[name=Orders,type=CONTAINER,id=3cd66458-403a-4db2-a922-d31ed28e4817] Subject[name=GOOG,type=CONDUIT,id=ddc2a6cc-a63b-48e3-a82e-7499f39cdd13] Subject[name=Sell,type=CHANNEL,id=ade6b57a-61be-4d40-a1c0-093023f2799d] |