The language used in Cloudfier is UML, via TextUML, a textual notation for UML. This page is not a replacement for the TextUML documentation (primarily the docs on structural and behavioral modeling), but it instead demonstrates how to use that notation when creating Cloudfier apps. Also, make sure you are familiar with the concepts used in Cloudfier apps.
Before you start, make sure you sign into Cloudfier, so you can put what you learned to practice right away!
mdd.properties
The mdd.properties file is a project configuration file. Any Cloudfier project must have this file, alongside with the actual application source files. The file is created automatically for you if you use the cloudfier init-project command, but you may also add it by hand. It should look like this:
mdd.enableExtensions=true
mdd.enableLibraries=true
mdd.enableTypes=true
mdd.extendBaseObject=true
# enables logging it as guest
mdd.application.allowAnonymous=true
# application title and name
mdd.application.title=My New To-Do Application
mdd.application.name=todo-app
Entity and properties
This simple app shows an entity with a couple of properties, one required (name) and another optional (birthDate):
role class Customer
attribute name : String;
attribute birthDate : Date[0,1];
end;
end.
As you can tell, Cloudfier maps TextUML/UML classes to entities. Note also that Cloudfier currently does not support multivalued properties.
Role entities
Any application needs to keep information about their users: for example, employees in an expense reporting application, or customers in an e-commerce application. The entities (classes) that represent roles of users in an application must be marked with the role modifier.
Relationships
This app is a bit more elaborate and shows three entities connected via two relationships, one, mandatory and single (Expense’s category), and another, multiple and optional (Employee’s expenses).
class Category
attribute name : String;
end;
class Expense
attribute amount : Double;
attribute date : Date;
reference category : Category;
end;
role class Employee
attribute name : String;
reference expenses : Expense[*];
end;
end.
Relationships can be defined in other ways. See the TextUML documentation on associations for more information.
Actions
The app below shows actions defined on an entity:
class Account
attribute number : String;
attribute balance : Double;
operation deposit(amount : Double);
begin
self.balance := self.balance + amount;
end
operation withdraw(amount : Double);
begin
self.balance := self.balance - amount;
end;
end;
end.
Queries
Queries are modeled as static query operations (use the query keyword instead of operation) that return a set of entity instances. The example below shows a query:
attribute severity : Severity;
static query bySeverity(toMatch : Severity) : Issue[*];
begin
return Issue extent.select((i : Issue) : Boolean { i.severity == toMatch });
end;
State Machines
The app below shows a state machine defined on an entity:
class Order
readonly attribute orderDate : Date := {Date#today()};
attribute customerName : String;
attribute orderStatus : Status;
operation complete();
operation process();
statemachine Status
initial state New
transition on call(process) to Processing;
end;
state Processing
transition on call(complete) to Completed;
end;
terminate state Completed end;
end;
end;
end.
Data types
The following built-in data types are avaiable:
- Integer
- Double
- Date
- String
- Memo
- Boolean
You can also use enumerations and other entity types.
Services
Services are provided/required via ports which are explicitly connected via connectors.
Declaring a service
A service declaration involves declaring:
- a service interface
- a service implementation
operation multiply(v1 : Double, v2 : Double) : Integer;
end;
class MultiplicationService implements Multiplier
operation multiply(v1 : Double, v2 : Double) : Double;
begin
return v1 * v2;
end;
end;
Declaring a service client
In order to require a service, you need to declare a requiring port typed by the required interface.
required port multiplier : Multiplier;
attribute quantity : Integer;
attribute unitPrice : Double;
derived attribute totalPrice : Double := {
self.multiplier.multiply(quantity, unitPrice)
};
end;
Hooking up the client and provider
Finally, to hook clients to a service implementation, you need to offer an instance of that service through a port provided by a component and connect it to the requiring port:
composition orderItems : OrderItem[*];
composition multiplicationService : MultiplicationService;
provided port multiplier : Multiplier
connector orderItems.multiplier, multiplicationService;
end;
In this example, the OrderApp component contributes an instance of MultiplicationService as an implementation of the Multipler interface. Then, it declares a providing port called ‘multiplier’ and connects it to requiring port also named ‘multiplier’ in OrderItem.
Note that an application must have all required ports connected to some compatible providing port, or a compilation error will occur.
Signals
Signals are especial data types describing events.
Declaring a signal
attribute employeeName : String;
attribute amount : Double;
attribute description : String;
attribute expenseId : Integer;
end;
Sending a signal
You usually send a signal to some service object accessed via a required port.
begin
send ExpenseApproved(
employeeName := self.employee.name,
amount := self.amount,
description := self.description + "(" + self.category.name + ")",
expenseId := self.expenseId) to self.expensePayer;
end;
Receiving a signal
The example below shows a class that receives several kinds of signals and reacts by sending another signal.
reception(n : IssueReported);
begin
send EMailMessage(
subject := "New issue: " + n.issueKey + " - " + n.summary,
body := n.description,
\to := n.userEmail,
\from := "shipitplus@cloudfier.com") to self.emailer;
end;
reception(n : CommentAdded);
begin
send EMailMessage(
subject := "New comment to: " + n.issueKey + " by " + n.author,
body := Memo#fromString(n.author + " said: '" + n.comment + "'"),
\to := n.userEmail,
\from := "shipitplus@cloudfier.com") to self.emailer;
end;
reception(n : IssueResolved);
begin
send EMailMessage(
subject := "Issue resolved: " + n.issueKey,
body := Memo#fromString(n.issueKey + " has been resolved as: " + n.resolution),
\to := n.userEmail,
\from := "shipitplus@cloudfier.com") to self.emailer;
end;
required port emailer : Emailer;
end;
What next?
Hope you had fun trying these examples in Cloudfier. Now extend them with your own ideas! Or for more examples, try the Example Apps.