Console application for managing
.tuc
files.
Typed Use-Case
It is basically a use case definition, for which this console application can generate PlantUML diagram, where all services are domain specific type safe.
We have a DDD based micro service architecture, where most of the services have an asynchronous communication between them (mostly through event streams) with a domain specific ubiquitous language.
And we need to document the use-cases done by those services.
For now, we use a PlantUML directly, but it is a lot of work, so we decided to create a language to help us with that - TUC.
The process of this use-case is a collecting interactions by the users.
User interacts with the GenericService, which sends an interaction to the interaction collector service. Interaction collector service identify a person and accepts an interaction.
It is just a simplified real-life process.
Note: All files are in the example dir.
// Common types
type Id = UUID
type Stream<'Event> = Stream of 'Event list
type StreamHandler<'Event> = StreamHandler of ('Event -> unit)
// Types
type InteractionEvent =
| Confirmation
| Rejection
type InteractionResult =
| Accepted
| Error
type IdentityMatchingSet = {
Contact: Contact
}
and Contact = {
Email: Email option
Phone: Phone option
}
and Email = Email of string
and Phone = Phone of string
type Person =
| Known of PersonId
| Unknown
and PersonId = PersonId of Id
// Streams
type InteractionCollectorStream = InteractionCollectorStream of Stream<InteractionEvent>
// Services
type GenericService = Initiator
type InteractionCollector = {
PostInteraction: InteractionEvent -> InteractionResult
}
type PersonIdentificationEngine = {
OnInteractionEvent: StreamHandler<InteractionEvent>
}
type PersonAggregate = {
IdentifyPerson: IdentityMatchingSet -> Person
}
type ConsentManager = {
GenericService: GenericService
InteractionCollector: InteractionCollector
}
tuc Identify person on interaction
participants
ConsentManager consents
GenericService as "Generic Service"
InteractionCollector consents as "Interaction Collector"
[InteractionCollectorStream] consents
PersonIdentificationEngine consents as "PID"
PersonAggregate consents
GenericService
InteractionCollector.PostInteraction
do create an interaction event based on interaction
InteractionEvent -> [InteractionCollectorStream]
[InteractionCollectorStream]
PersonIdentificationEngine.OnInteractionEvent
PersonAggregate.IdentifyPerson
do
normalize contact
identify a person based on the normalized contact
if PersonFound
do return Person
else
do return Error
Console app will generate following result.puml
based on the Domain types (from domain.fsx
) and the definition.tuc
file, where the use-case is.
For example there is a
GenericService
InteractionCollector.PostInteraction
call in the definition.tuc
file.
It will find an InteractionCollector
type and its PostInteraction
method signature
type InteractionCollector = {
PostInteraction: InteractionEvent -> InteractionResult
}
and generate
GenericService -> InteractionCollector : PostInteraction(InteractionEvent)
activate InteractionCollector
InteractionCollector --> GenericService: InteractionResult
deactivate InteractionCollector
out of it.
@startuml definition
== Identify person on interaction ==
box "ConsentManager"
actor "Generic Service" as GenericService <<Consents>>
participant "Interaction Collector" as InteractionCollector <<Consents>>
end box
collections "InteractionCollectorStream" as InteractionCollectorStream <<Consents>>
participant "PID" as PersonIdentificationEngine <<Consents>>
participant "PersonAggregate" as PersonAggregate <<Consents>>
activate GenericService
GenericService -> InteractionCollector ++: PostInteraction(InteractionEvent)
hnote over InteractionCollector
do: create an interaction event based on interaction
end hnote
InteractionCollector ->> InteractionCollectorStream: InteractionEvent
InteractionCollectorStream ->> PersonIdentificationEngine: OnInteractionEvent(InteractionEvent)
activate PersonIdentificationEngine
PersonIdentificationEngine -> PersonAggregate ++: IdentifyPerson(IdentityMatchingSet)
hnote over PersonAggregate
do:
normalize contact
identify a person based on the normalized contact
end hnote
alt PersonFound
hnote over PersonAggregate
do: return Person
end hnote
else
hnote over PersonAggregate
do: return Error
end hnote
end
PersonAggregate --> PersonIdentificationEngine --: Person
deactivate PersonIdentificationEngine
InteractionCollector --> GenericService --: InteractionResult
deactivate GenericService
@enduml
First compile
fake build target release
Then run
dist/<architecture>/TucConsole help
List commands
dist/<architecture>/TucConsole list
______ __ __ _____ _____ __
/_ __/ / / / / / ___/ / ___/ ___ ___ ___ ___ / / ___
/ / / /_/ / / /__ / /__ / _ \ / _ \ (_-</ _ \ / / / -_)
/_/ \____/ \___/ \___/ \___//_//_//___/\___//_/ \__/
==================================================================
Usage:
command [options] [--] [arguments]
Options:
-h, --help Display this help message
-q, --quiet Do not output any message
-V, --version Display this application version
-n, --no-interaction Do not ask any interactive question
-v|vv|vvv, --verbose Increase the verbosity of messages
Available commands:
about Displays information about the current project.
help Displays help for a command
list Lists commands
domain
domain:check Checks given domains.
tuc
tuc:check Checks given tuc.
tuc:generate Compile a tuc with domain types and generates a use-case in the PlantUML format out of it.
First run:
paket install
fake build
or fake build target watch
List commands
bin/console list
Run tests locally
fake build target Tests