docker-compose.yml
that can be used to make starting both tools together. Yet, Docker will start Schemata on your local development computer. If you prefer to run Schemata on another computer, such as a team or organizational server, there is no need to start Schemata locally.docker-compose
file created by the VLINGO XOOM team, you can initialize Designer simply using these two commands:docker-compose
, XOOM Schemata also will be started and initialized, and can be accessed at http://localhost:9019. Learn more about the XOOM Designer/Schemata integration here.--name xoom-schemata
so that you can reference it in Designer-Schemata integration options:curl
.zip
tar
VLINGO_XOOM_DESIGNER_HOME
indicating the absolute path for the uncompressed folder. Additionally, on Unix-based operating systems, it is necessary to enabled read and execute access on executable script as following:Register
command message is sent to the Investor
Aggregate, which is a new instance and is initialized by handling the Register
messageInvestor
runs any business rules, validates the command, and applies the new InvestorRegistered
eventAccountClosed
event which is consumed by the Investor Context (the current model context under design)Suspend
command that is sent as a command message to the Investor
Investor
accepts the Suspend
command message, validates it, and applies the new InvestorSuspended
eventInvestor
Aggregate is now the focus of this part of the model. The Investor
first receives an initializing Register
command message and emit the InvestorRegistered
event. Following this, at some point the AccountClosed event is received from the external Accounts Context, translated to the Suspend
command message, and dispatched to the Investor
Aggregate. The Investor
subsequently validates the Suspend
command message and causes the InvestorSuspended
event to be emitted.Investor
model element stack in the previous figure. Thus, the slices through each subsystem, or Bounded Context, are appropriately visualized. This is shown in the following figure:pom.xml
standard groupId
. An example of such is io.vlingo.brokerage
, which will be inserted in the project's pom.xml
as the following: <groupdId>io.vlingo.brokerage</groupId>
pom.xml
standard artifactId
. An example of such is investor
, which will be inserted in the project's pom.xml
as the following: <artifactId>investor</artifactId>
. This also serves as the name of the Bounded Context, such as Investor Context.pom.xml
standard artifact version
, which is a semantic version. An example of such is 1.0.0
, which will be inserted in the project's pom.xml
as the following: <version>1.0.0</version>
.io.vlingo.brokerage.investor
, which will be used to derive all other package names in the generated project. The example project would have: io.vlingo.brokerage.investor.infrastructure
and io.vlingo.brokerage.investor.model
, which serve as the two major layers of the Compressed Ports and Adapters Architecture.State
appended. For example, the Aggregate named Investor has a state type named InvestorState
.+ NEW AGGREGATE
button to open the Aggregate dialog box. This is where you declare the details about an Aggregate type to be included in your domain model. When the dialog displays, fill in the appropriate sections of the dialog.Investor
, the implementing class is named InvestorEntity
.State
. If the protocol name is Investor
then the state type name is InvestorState
. The state type is the only value directly held by the Root Entity. The fields of the state type are also known as attributes or properties. Each field has a name and a type. In the above example the Investor
type has three declared state fields, one named name
of typeFullName
, one named suspended
of type boolean
, and another named balanceSheet
of type BalanceSheet
.List<T>
or Set<T>
, or it can be left a "bare" type, which means without a collection.name
of an Investor
is Zoe Doe, and when Zoe gets married, her name changes to Zoe Jones-Doe. The InvestorState
would contain the same first/given name of Zoe, but the family/sir name of Doe would be replaced with Jones-Doe.changeName(FullName)
command message, which causes the state to transition.Investor
could be suspended from trading, there could be a suspended
field of type boolean
in addition to the name
.FullName
type is a Value Object, which can be defined by clicking the button named + NEW VALUE OBJECT
that sits just below the Aggregate Name field. You define the Value Object by giving it a type name and some number of fields/attributes. For example, FullName
has the following definition:BalanceSheet
transactions
List<Transaction>
BuyDetails
symbol
String
price
Money
shares
int
FullName
first
String
last
String
Money
value
String
Transaction
datetime
DateTime
amount
Money
type
TransactionType
TransactionType
value
int
EDIT VALUE OBJECT
button.balanceSheet
of type List<Money>
, and may have wondered if it really belongs as a member of the Investor
. Let's say initially it seemed like a good idea, but then later it seemed much less desirable. It likely belongs on an Account
, which isn't even in our Investor Context.List<T>
or Set<T>
of any type, such as List<Money>
.balanceSheet
from the Investor
type.InvestorRegistered
. id
of type String
. In the case of InvestorRegistered
, it will contain both the id
and the name
of type FullName
of the Investor
.suspend()
needs no parameters, and the resulting InvestorSuspended
event need not have any state other than the Investor
unique identity. Still, the event indicates that a state transition has occurred, and ultimately requires setting the suspended
field to true
. Since no state transition is obvious to the Designer and its code generation step, the post generation source code will require some modifications. This is explained in further detail below in this same subsection.Investor
type, one such message with a corresponding message handler is the method register(FullName name)
. register
register()
method accepts drop-down. In this case the only parameter is name
. For this message handler, as explained next, there is no need to select the id
as a parameter.register()
method is a creational or factory method because it is used to initialize the Investor
state. Thus, toggle on the option: Involves creation of entity?credentials
, which has the type Set<Credential>
:credentials*
void declareAll(List<Credential> credentials)
credentials#
void merge(List<Credential> credentials)
credential+
void declare(Credential credential)
credential-
void discard(Credential credential)
add-all
, merge
, add
, remove
are implementation dependent. The actual implementation can be as desired. The idea behind the symbols is to provide standard operations that are common with collections.id
to be passed with this creational register()
message because the unique String id
is always provided with construction. You can see that in the following code that is generated by the XOOM Designer:id
available from construction, it would be rare if the id
where passed as a parameter, if ever.Investor
could be suspended from trading, the non-parameter suspend()
message could be sent, which implies that a boolean suspended
field/attribute on the InvestorState
would be transitioned to true
. // TODO
comment generated because the developer must determine and write the code for what the state transition means. As seen above, this involves replacing the // TODO
comment in the suspend()
function with a single line of code. As a reminder, here's a small snippet of code that shows the edits:InvestorEntity
source used to transition state:InvestorSuspended
event is applied (i.e. emitted) the InvestorState
is temporarily transitioned. This is generally always the case because the newly transitioned Aggregate state will be used to provide the fields/attributes to the event. In the case of InvestorSuspended
this is not absolutely necessary because the event requires only the unique identity (i.e. id
) of the Aggregate. The state is permanently (see the below Info box) transitioned by the applyInvestorSuspended()
after the initial apply()
is confirmed.Investor
suspension can be reversed later. The point of "permanently" is that the InvestorSuspended
event will be applied for that moment in time when the Aggregate state is reconstituted. If an event that reverses the suspension occurs later in time, it will also be reapplied during reconstitution for the point in time that happened later.apply...()
method it is persisted asynchronously without the Aggregate's knowledge.StatefulEntity
subclass as opposed to an EventSourced
subclass, the state can be applied without an event. You determine this by you selection in Step 4: Persistence (see below). The following selection causes all Aggregates to extend StatefulEntity
:EventSourced
:suspend()
message handler's declaration.InvestorSuspended
, are used, the Designer has no hints to transition the state on its own. (There will be future attention given to this by tooling, but currently state transition hinting is not supported.) There are two places where this must be cared for.suspended
is false
, but the Designer will inevitably generate tests with true
, because we consider that the positive state perspective is more common. All test cases that consider the default state as true
must be adjusted to false
instead.false
to true
require adjustment, but actually the default of true
is also used in those cases./investors
.POST
to a collection will be the same as the Root Path, in which case *
can be used to indicate that Path is the same as Root Path. Otherwise, a fully identified entity URI is required to which a request will PUT
, PATCH
, or DELETE
the resource. This is seen in the second example.POST
, GET
, PUT
, PATCH
, DELETE
, HEAD
, or OPTIONS
.{projectArtifactId}-exchange
. Depending on the architectural messaging mechanism in use, this might be called a topic name rather than an exchange name. For example, RabbitMQ uses exchanges and Kafka uses topics. Having that in mind, after the project is generated, you can edit the XOOM properties file and, if you find it necessary, change the default exchange name.Org:Unit:Context
pattern for the Registry Path. It serves as the prefix path to where the event schemas will be registered in XOOM Schemata. The Org is the name of the organization that owns the schema. The Unit is the business unit or division within the organization. The Context is the name of the fully-qualified package name or namespace of the Bounded Context of which the schemas are a part. Note that the Context will likely be the same used in the Context page for the Base Package Name. This path can be created and selected using the Schemata integration. Click theOrg:Unit:Context
field to use the Schemata UI.Org:Unit:Context
is created and selectedaccounts-topic
. Each schema type to be received on that topic is declared on separate lines, followed by the Aggregate's message handler method that will receive the resulting command. In the case show above, the Accounts Context can emit the AccountClosed
event when an investor discontinues their relationship with the brokerage, or if a corrective measure is taken by the brokerage itself. The fully-qualified schema registry path is:VLINGO
, the Unit is named Brokerage
, and the Context is named from the project's base package name io.vlingo.brokerage.accounts
. Following the basic path to the Context, the Schema Name is provided as AccountsClosed
and the Schema Version is 1.0.0
. This event schema and version is registered with XOOM Schemata, selected through the UI integration, and will be automatically pulled from the registry as a Java class source file. The included schema source is an unmodifiable dependency. Thus, type safety is retained across the system, even in messages exchanged between subsystems./src/main/resources/xoom-turbo.properties
in your generated project directory. The following shows how this works for our running example:Investor
context consumes events from Account
context. So, the next step is to click on the Account
topic and bind the Investor
listening queue. As the following image shows, the Account
topic initially publishes messages only to its own listening queue.
Investor
exchange as seen in the following screenshot.Account
exchange will be delivered to the Investor context.Account
exchange can now be consumed by the Investor
service.EventSourced
, meaning that they will use Event Sourcing persistence.StatefulEntity
, meaning that persistence will be to key-value (i.e. NoSQL) database.StatefulEntity
full state, or from events emitted out of EventSourced
entities. Actually StatefulEntity
types may also project Query Model views from events. Yet, this requires every command message handler to emit and event, which might go against the desired model design.zip-download
. This is the only possible choice when running XOOM Designer from a Docker image: