I am diagramming a situation similar to Git, where you can have a single file in multiple states at once (i.e. a file with staged changes and unstaged changes). In this scenario, I have three main states:
Unedited file
Edited/unstaged file
Staged file
Is it possible to show that a single file is in both state 2 and 3 without duplicating all the state information into another state (i.e. State 4. Staged and edited/unstaged file). Here is a simplified diagram:
State machine basics
In your SM , there is no region, no composite state, nor submachines. There can therefore only be at most one state active at a given time. It's not written exactly like that, but it emerges from the semantics of the SM in the UML specs, inter alia:
A behavior StateMachine comprises one or more Regions, each Region containing a graph (possibly hierarchical) (...). A particular execution of a StateMachine is represented by a set of valid path traversals through one or more Region graphs, triggered by the dispatching of an Event occurrence (...). Due to its event-driven nature, a StateMachine execution is either in transit or in state, alternating between the two. It is in transit when an event is dispatched that matches at least one of its associated Triggers.
The graph traversal mechanism through transitions and states make it clear that two states cannot be active at the same time.
More complex state machines
State machines can be much more complex. First of all, a SM can be made of Regions:
A Region denotes a behavior fragment that may execute concurrently with its orthogonal Regions. Two or more Regions are orthogonal to each other if they are either owned by the same State or, at the topmost level, by the same StateMachine.
Furthermore composite states may have sub-states: so if this state is active, a substate of it may get active as well. And submachines may refer to even more complex situations.
In such comple SM, the curent state of the machine is in reality a configuration of several compatible active states in the hierarchy of states across the active regions.
What are your requirements?
Whenever, you feel that several states could be relevant at the same time, you must therefore decompose your states further, and identifying those who are related (e.g.: potential substates) and those who are independent (orthogonal regions).
In other words, if Unedited file, Edited/unstaged file and Staged file are not sufficient, independently of GIT semantic, you could think of:
region 1: Undedited, Edited and region 2: Staged, Unstaged, which gives 4 potential configurations: Undedited/Staged, Edited/Staged, Undedited/Unstaged, and Edited/Unstaged.
if some combinations are not possible (e.g. Undedited/Staged) youd could think of Unedited (implicitely always unstaged) and Edited as composite state with substates Staged, Unstaged, which gives the potential configurations Edited.Unstaged and Edited.Staged
Or maybe there are some missing states: e.g. new (always implicitely unstaged) committed-a-first-time, which could have 2 reagions (as in the first bullet above)
etc...
What could guide your state analysis is to find an invariant condition that best describes the state in a unique and unambiguous way.
History
SM history will not resolve your concurrent state issue:
The concept of State history (...) is a convenience concept associated with Regions of composite States whereby a Region keeps track of the state configuration it was in when it was last exited. This allows easy return to that same state configuration, if desired, the next time the Region becomes active (...), or if there is a local Transition that returns to its history.
Conclusions
The solution to your problem may be outside theSM. For instance, a file which is edited and then staged, has two versions: the current version on the local drive that is editable, and the staged unmutable version in the GIT repository. In this case you have indeed the edited staged version active and the unedited (in comparison to the staged) version. The two concurent states relate to different objects, each having its own SM.
I believe your problem stems from the fact that you are trying to model two different state machines as one.
The first is the Unedited/Edited State Machine, the second is to do with Staging. The File will be the subject of both State Machines, but the states in these machines are independent of one another: Whether the File is Staged or Un-staged is not dependent on the File having been Edited; it is dependent on whether you as a user have told Git to Stage the File or not. You can Stage a File that is Unedited, and you can choose not to Stage a File that is Edited.
The editor displaying a "E" or an "S" against the name of the File is a choice about how you want to communicate the states a File is in. I assume that a File that is not Edited, but is Staged, would have an "S" displayed by its name, regardless of the face that the File had not been Edited. Displaying these symbols is behavioural logic that is not dictated by the states, but by the interpretation of them and the possible combinations of them.
From the State diagram you have included, I'm not sure, but maybe are you trying to express the process of development in a State Machine? It would be common to Edit and then Stage, but you could do it the other way around. Have you thought about using an Activity Diagram instead to express the process?
Related
What are the differences and similarities between the state machine diagram and flowchart?
So far I found that the state diagram shows us the actual change in the state, not the process or commands like in the flowchart.
In a state diagram, the nodes are states and the arrows correspond to something that happens that triggers a change of state.
In a flow chart, the nodes are actions or decisions, and the arrows correspond to the flow of control, i.e. what happens next. Flow chart are not UML. The closest UML diagram is an activity diagram that allows you to meodell more precisely everything you can model with a flow-chart and more (since the arrows can also represent an object flow).
Both can be complementary: an action/decision in a flow-chart could trigger events that cause changes of state. But it's not necessarily one for one: an action may cause several state changes without any evidence in the flow-chart that these may happen.
Short and simplified example:
Imagine an Order object. It may have the state received, delivey in preparation, delivery complete, invoiced. Each of these state tells what can happen next with the order. That'll be a topic for astate diagram.
Imagine a flowchart. These are different actions happening in sequence: Get an order from customer, Pick items of the orders from the inventory, Send items to the customer, Are all items sent?, if yes, Prepare and send invoice, if no, Find missing items and then go back to Pick items ... and continue from there.
As you see: both can tell the same story, but from a different angle with different details.
As you mentioned, a State Machine Diagram focus on display from which state to which state the execution goes based on the input.
Although a State Machine can be handled as a specialized form of a flow chart / activity chart.
I was wondering if you could help me clarify two aspects regarding multi-instance state machines.
First question
Consider an example state machine SM1 containing one state A:
On the left, transition start creates a new instance of the state machine. Transition stop terminates the instance.
There can be multiple instances of state machine SM1 running in parallel, e.g. 5 instances.
Now, what I want is a transition that would terminate ALL state machine SM1 instances that are running at the given time.
E.g. we create five state machines A and then transition stopALL would terminate ALL of them at once.
Is such behaviour permitted by UML specification? If yes, is there a graphical notation to unambiguously represent such a behaviour? I could not find the answer in the UML specification document.
Second question
Consider a multi-instance state machine SM2 with state A and one transition startStop:
The behaviour of the transition is as follows: upon firing, the transition creates a new instance of SM2 and terminates an existing one.
Is such behaviour permitted by the specification? Is there an unambiguous graphical way to express such a behaviour?
There is no special UML method but standard UML tools are absolutely sufficient. You need to broadcast a terminate signal (on state machine diagram you can represent it by behaviour on a transition that should terminate other instances). Then you just need to model that on terminate reception the state machine goes to final state.
Terminate All Example
Note that the behaviour after slash (/) is a behaviour invoked on a state transition i.e. when a state machine changes it's state into final the behaviour sendStopAllSignal is invoked which in turn should be described on a class diagram (with probably corresponding activity diagram)
Similarly you need to have a receiveStopAllSignal behaviour included in class diagram.
Terminate Existing At Start
This is similar situation - you need to have both sendStopSignal and receiveStopSignal modelled elsewhere.
Note however that such naming convention (sendAbcSignal for behaviour of sending/broadcasting signal Abc and receiveAbcSignal for behaviour as a reaction to Abc signal reception) is quite common, useful and self-explanatory (i.e. you probably won't model a separate action diagrams for those behaviours unless some extra logic is needed there).
I'm learning DDD and Hexagonal architecture, I think I got the basics. However, there's one thing I'm not sure how to solve: how am I showing data to the user?
So, for example, I got a simple domain with a Worker entity with some functionality (some methods cause the entity to change) and a WorkerRepository so I can persist Workers. I got an application layer with some commands and command bus to manipulate the domain (like creating Workers and updating their work hours, persisting the changes), and an infrastructure layer which has the implementation of the WorkerRepository and a GUI application.
In this application I want to show all workers with some of their data, and be abe to modify them. How do I show the data?
I could give it a reference to the implementation of WorkerRepository.
I think it's not a good solution because this way I could insert new Workers in the repository skipping the command bus. I want all changes going through the command bus.
Okay then, I'd split the WorkerRepository into WorkerQueryRepository and WorkerCommandRepository (as per CQRS), and give reference only to the WorkerQueryRepository. It's still not a good solution because the repo gives back Worker entities which have methods that change them, and how are these changes will be persisted?
Should I create two type of Repositories? One would be used in the domain and application layer, and the other would be used only for providing data to the outside world. The second one wouldn't return full-fledged Worker entities, only WorkerDTOs containing only the data the GUI needs. This way, the GUI has no other way to change Workers, only through the command bus.
Is the third approach the right way? Or am I wrong forcing that the changes must go through the command bus?
Should I create two type of Repositories? One would be used in the domain and application layer, and the other would be used only for providing data to the outside world. The second one wouldn't return full-fledged Worker entities, only WorkerDTOs containing only the data the GUI needs.
That's the CQRS approach; it works pretty well.
Greg Young (2010)
CQRS is simply the creation of two objects where there was previously only one. The separation occurs based upon whether the methods are a command or a query (the same definition that is used by Meyer in Command and Query Separation, a command is any method that mutates state and a query is any method that returns a value).
The current term for the WorkerDTO you propose is "Projection". You'll often have more than one; that is to say, you can have a separate projection for each view of a worker in the GUI. (That has the neat side effect of making the view easier -- it doesn't need to think about the data that it is given, because the data is already formatted usefully).
Another way of thinking of this, is that you have a "write-only" representation (the aggregate) and "read-only" representations (the projections). In both cases, you are reading the current state from the book of record (via the repository), and then using that state to construct the representation you need.
As the read models don't need to be saved, you are probably better off thinking factory, rather than repository, on the read side. (In 2009, Greg Young used "provider", for this same reason.)
Once you've taken the first step of separating the two objects, you can start to address their different use cases independently.
For instance, if you need to scale out read performance, you have the option to replicate the book of record to a bunch of slave copies, and have your projection factory load from the slaves, instead of the master. Or to start exploring whether a different persistence store (key value store, graph database, full text indexer) is more appropriate. Udi Dahan reviews a number of these ideas in CQRS - but different (2015).
"read models don't need to be saved" Is not correct.
It is correct; but it isn't perhaps as clear and specific as it could be.
We don't need to create a durable representation of a read model, because all of the information that describes the variance between instances of the read model has already been captured by our writes.
We will often want to cache the read model (or a representation of it), so that we can amortize the work of creating the read model across many queries. And various trade offs may indicate that the cached representations should be stored durably.
But if a meteor comes along and destroys our cache of read models, we lose a work investment, but we don't lose information.
What happens in an UML state machine if the transition selection algorithm (TSA) finds two transitions that should both fire and the following holds true:
transition #1 ends directly in a state
transition #2 ends (intermediately) in a choice pseudostate
As both the transitions fire, they cannot be in conflict. Else they would not have been chosen by the TSA in the first place.
Now the following occurs:
As the choice is evaluated (after transition #2), it takes a path (transition) that would lead to the exit of an ancestor state of the source state of transition #2. Due to this exit of an ancestor state, a conflict with transition #1 occurs.
UML diagram showing such a situation
(improved according to the discussion with Thomas Kilian in comments to his (now deleted) answer)
... if Event_1 occurs and x < 0.
Questions
Is the state machine ill-formed or what is supposed to happen now?
Is it illegal for a transtions after a choice to exit a state? At least it doesn't play together well with the "transition execution sequence" (exit(s), transition behavior(s), enter(s)). As it would be exit, behavior, exit, behavior, enter(s) in that case. I could not find anything about that in the UML Superstructure Specification (chapter 15). But then everything about orthogonal regions is very vaguely described in that document...
Motivation
I am asking the question out of the perspective of a library implementer. So my focus is not on how to design this situation in a nicer manner. I need to figure out how to deal with this situation correctly (or I need to know that it is an illegal situation).
This is one reason why there is a "Precise Semantics of UML State Machine" at the OMG...
First, it is legal for most transitions to exit a state. There are a few restrictions in the UML spec, but none that would concern your design.
You should always have a single state that is potentially active within any state machine region. As it stand, you have an implicit region in your "StateMachine1" that contains both the "X" and"Y" states. This would mean that your state machine could be in both "StateMachine1::X" and "StateMachine1::Y" states at the same time! As you probalby already know, orthogonal states are there do deal with such a situation. Perhaps you need to bring "X" and "Y" within state StateMachine1::S' regions?
Also a warning that by default in UML, states have shallow history. This means that, as it stands, the initial transitons within StateMachine1::S' regions will only be taken once. If you try to get back into "S" (I know, you don't have a transition for that - just speculating), it will go back to the last states (one per region) before if left the state, which, after event1, would be "A" and "E".
It would be difficult to comment more without knowing what you are trying to model, but I hope this helps.
I am trying to properly understand regions as well as fork and join pseudostates in UML State Machines.
All examples I find are quite simple and I am not quite sure what is legal and what is not.
The UML Superstructure Specification (15.3.14 Transitions) states
The tail of a compound transition may have multiple transitions originating from a set of mutually orthogonal regions that
are joined by a join point.
Does this mean, that a join may only be placed at the end of a compound transition?
Does this mean in consequence, that a join may only be followed by a state but not by a pseudostate?
It also states:
The head of a compound transition may have multiple transitions originating from a fork pseudostate targeted to a set of
mutually orthogonal regions.
Does this mean, that a fork may only be placed at the beginning of a compound transitions (i.e. directly following a state but not after a pseudostate)?
Chapter 15.3.8 Pseudostate does not help me answering these questions. Neither the Constraints section nor the Semantics section make it any clearer to me.
join vertices serve to merge several transitions emanating from source vertices in different orthogonal regions. The
transitions entering a join vertex cannot have guards or triggers."
If the incoming transitions don't have triggers: How do the transitions ever fire?
Example:
Composite state S1: in region one S11 is active, in region two S12 is active. S11 and S12 have transitions leading to a join. How does the join ever get reached?
Edit:
I stopped reading to soon. The answer to this one is "Through a completion event". These transitions are "completion transitions".
Also:
When "mutually orthogonal regions" are mentioned: Do these regions have to be the children of the same composite states?
More general: Can states only be concurrently active if they are placed in different regions of the same composite state or could - after a fork - multiple states be active that
are located in totally different composite states?
Fork and join stem from Petri nets. Along that net you have a virtual token that travels from node to node along the transitions. A fork will multiply an incoming token to as many outgoing transitions as there are. Only when all those tokens have reached the next join the machine will continue with a single token (unless this forks again with multiple transitions - but inside the fork line there will be one single token). You can imagine the line that represents a fork/join as a (1-dimension) box which momentarily holds a single token. This single token exists at the moment when all incoming transitions hold a token. This token is then multiplied for all outgoing transitions. Firing the new tokens takes no time. It happens instantly when the internal token has been created from the incoming tokens.
Unfortunately this concept is not well known and many modelers used it the wrong way. But I guess if you get the concept of the traveling (multiplied) tokens you will get the idea.