featdoc
-
is a documentation-as-code tool, which generates wiki pages in Markdown syntax including Mermaid diagrams.
-
generates complete sequence diagrams from interacting systems, defined by simple rules
The example models a restaurant. From the source code, the resulting markdown is deployed to an Azure DevOps wiki, the Coffee-Wiki (and here is the sequence diagram above).
-
Create a
Universe
Universe UNIVERSE = new Universe();
-
Create a
System
System WAITER = UNIVERSE.system("waiter", "Waiter", "Waiter");
-
Define a
Rule
within a system (grouped byFeature
)
WAITER.feature("Coffee Serving") .rule(orderEspresso, CoffeeMachine.espresso, MobileOrderClient.createOrder, MobileOrderClient.addItemToOrder) .rule(customerWantsToPay, MobileOrderClient.createBill, Customer.receiveBill);
-
Create some more systems and rules
-
Create a
Scenario
Systems.UNIVERSE.scenario("Lunch-Customer (in a hurry)") .step(CUSTOMER, Waiter.orderEspresso) .step(CUSTOMER, Waiter.customerWantsToPay, "Today no credit cards");
-
Generate in one go wiki pages for each system and each scenario
Compare the hand-drawn domain image with the featdoc auto-generated system diagram.
-
The example WikiContext defines the mermaid block style, an optional preamble for all pages, the language (en or de), as well as some paths.
-
The example technical model is just one class. Larger models can be split up into multiple files, e.g. one per system.
-
Add feat as a test dependency
Maven Coordinates<!-- https://mvnrepository.com/artifact/de.xam/featdoc --> <dependency> <groupId>de.xam</groupId> <artifactId>featdoc</artifactId> <version>1.0.0</version> </dependency>
Check latest version: https://mvnrepository.com/artifact/de.xam/featdoc
-
Define your technical model
-
Create a
Universe
— see the Example
-
-
Generate wiki pages
-
wikiContext
: Configure resulting wiki syntax (Azure DevOps Mermaid Blocks vs. Normal) -
Run featdoc tool
FeatDoc.generateMarkdownFiles( universe, wikiContext );
-
-
Take generated wiki pages live
-
For Azure DevOps wiki: Upload via
git push
-
-
The core idea is to model isolated systems, each containing simple rules: Mapping one trigger to multiple actions. Each system has an API defined by messages, which are incoming (such as REST API calls) or outgoing (such as business events). Rules are grouped into features. This just makes organising them easier.
-
A scenario now merely defines an initial trigger, causing a chain reaction of systems sending messages to each other. As one rule can cause several actions, the result can be rendered tree-like or as a sequence diagram.
-
A message is produced by one system (or a scenario) and consumed by zero or more systems (as defined in its rules).
-
A message is defined by a system. E.g. the vendor of the system defines the message structure and contents. Also, the vendor will likely provides API docs for the message.
-
Typical messages are API calls (incoming & synchronous) or business events (outgoing & asynchronous).
Table 1. Kinds of Messages Direction Timing Examples incoming
synchronous
API call in a REST API
User interface input (e.g. click)
outgoing
synchronous
REST Callback/Web Hook
User interface result (any UI reaction)
incoming
asynchronous
Incoming event
outgoing
asynchronous
Outgoing (business) event
-
- How do I model conditional rule actions?
-
You can’t. But you can add a comment to a rule action, which will be visible in all generated views.
MYSYSTEM.feature("my feature") .rule(triggerMessage, optionalTriggerComment) .action(actionMesage, optionalActionComment) // more actions .build;
- How to represent scheduled calls?
-
Define a
System
called "Scheduler" with messages likeSystem SCHEDULER = UNIVERSE.system("scheduler", "Scheduler", "Scheduler"); Message schedulerEvery2Minutes = SCHEDULER.asyncEventOutgoing("Every 2 Minutes");
and invoke them from the scenario.
- How to model my systems?
-
-
Create all systems in one interface
interface Systems { Universe UNIVERSE = new Universe(); System CUSTOMER = UNIVERSE.system("customer", "Customer", "Customer"); System WAITER = UNIVERSE.system("waiter", "Waiter", "Waiter"); System CM = UNIVERSE.system("coffee", "Coffee Machine", "CoffeeMachine"); System MOC = UNIVERSE.system("moc", "Mobile Order Client", "Mobile"); System POS = UNIVERSE.system("pos", "Point of Sale System", "POS"); System ACC = UNIVERSE.system("accounting", "Accounting System", "Accounting"); }
-
Model each `System’s rules within an interface, like this
/** Point-of-Sale System */ interface PosSystem { Message createOrder = POS.apiCall("Create order for table"); Message searchOrdersForTable = POS.apiCall("Search order of given table"); Message addItemToOrder = POS.apiCall("Add item"); Message addTaxesToOrder = POS.apiCall("Add taxes"); static void define() { POS.feature("Tax Integration") .rule(addItemToOrder, AccountingSystem.calculateTax, addTaxesToOrder); } }
-
-
Markdown: syntax, Github-flavored Markdown, syntax in Azure DevOps Wiki
-
Mermaid: docs, online editor
-
featdoc: details.adoc