Role-based access control for UML: the TextUML-Cloudfier approach

Cloudfier is an approach for building business applications, and since role-based access control (RBAC) is such an important thing for any business application, Cloudfier is bound to provide support for modeling what users can do to application objects. Here is our plan.

[There are several proposals by third-parties on how to do security with UML (remember, TextUML is UML), but the OMG itself has not officially adopted any so far. So, since there is no standard and no clear 3rd-party winner, I decided I might as well make up my own approach, tailor-made for the needs of business applications.]

The gist of the idea:

  1. classes, attributes and operations may declare “access constraints”, which are UML constraints specialized on describing how accessible an element (in practice, classes, attributes and operations);
  2. those constraints allow specific capabilities (like create and destroy instances of a classes, read and write attribute values or associations links, and call operations);
  3. access constraints are defined for one or more user roles – one element may have multiple constraints, matching different roles;
  4. the constraint specification may be an expression that (typically) will take the current logged-in user/actor in account in order to decide access should be allowed.

Profile changes

TextUML models, by default, can count on a UML profile called mdd_extensions. This profile defines several extensions to the base UML standard, for things such as allowing one to mark a class as a test class, elements as debuggable, activities as closures, blocks as initialization blocks, type casts, attribute derivations etc. The mdd_extensions profile was enhanced to support the idea of role classes and access constraints

Role classes

A role class is a class that represents a role an actor can play. Here is the definition of the stereotype:


(* A role class is a class that represents the role for a user. *)
stereotype Role extends UML::Class
end;

Access constraints

Access constraints are constraints with the “Access” stereotype applied to them. The stereotype allows setting:

  • the roles affected – user roles that are covered by this constraint;
  • the capabilities allowed – what users with one of the roles can do. Values are values of the AccessCapability enumeration.

The constraint specification, which is a boolean value specification (could be just a constant, or a complex expression), determines (in addition to the user roles) whether the constraint applies. See the source that defines those extensions:

stereotype Invariant extends UML::Constraint
end;

(* You can declare access constraints on any element. *)
stereotype Access specializes Invariant
    property roles : Class[1,*];
    property allowed : AccessCapability[*];
end;

enumeration AccessCapability
    /* Objects */
    Create; 
    Delete;
    /* Attributes/links */
    Read;
    Update;
    /* Operations */
    Call;
end;

Notation changes

The notation changes were:

  • you can now declare a class as a role class by using the ‘role‘ modifier (or you can apply the Role stereotype directly if you prefer);
  • you can now use the ‘allow‘ keyword to declare access constraints on objects, attributes and operations.

Here is an example that should make things clear:


model banking;

  role class AccountOwner end;
  role class BranchManager end;
  role class AccountManager end;
  role class SecurityOfficer end;

  class Branch
    reference manager : BranchManager;
  end;

  class BankAccount
    allow BranchManager, AccountManager create, delete;
    reference owner : AccountOwner;
    reference branch : Branch;
    attribute balance : Double
        allow AccountOwner read { System#user() == self.owner } 
        allow BranchManager read { System#user() == self.branch.manager } 
        allow SecurityOfficer read;
    operation withdraw(amount : Double)
        allow AccountOwner { System#user() == self.owner }; 
    operation deposit(amount : Double)
        allow AccountOwner { System#user() == self.owner }; 
  end;
end.

For simplicity, I omitted details on the role classes, but they are normal classes otherwise and can have any attributes you may want, may specialize other classes etc.

But the use of the “allow” keyword to declare access constraints should be clear. If it isn’t to you, please provide feedback, here or on the project issue tracker.

Great, when can I use this?

TextUML Toolkit users: you should be able to use this if you update the plug-in in Eclipse now.

Cloudfier users: Cloudfier needs to honor the new access control features when running the model natively, or generating code, and some work is required before that happens, so you will need to wait a little longer to use this feature in your Cloudfier applications.

No matter which tool you use (or even if you don’t use any of them), if you have any opinion on the choices made, your feedback is really quite welcome.

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Online UML class and statechart diagrams for your Cloudfier apps

Starting today, you can visualize your Cloudfier applications using the UML graphical notation, as both class and statechart diagrams. This feature was one of the most requested when I am presenting Cloudfier. People get that the textual notation (TextUML) is better for actually building the application, but diagrams are useful for someone new to the application to quickly get the gist of it.

How to show Cloudfier applications as UML diagrams

Since Cloudfier shows the diagram for the deployed version of the application, not what you are editing right now, you need to deploy the application first. In order to deploy, you can use the app-deploy or full-deploy commands. If the application is already deployed, you can use the cloudfier info command to just show the links to diagrams:

cloudfier info cloudfier-examples/car-rental

app-info

Then just click one of the links to class or statechart diagrams. This will open the diagram in a new tab. here is an example of a diagram URL:

The URL determines what package we are rendering and what elements should be rendered (via query parameters). By default, class diagrams are showing classes with attributes and operations (live class diagram example), and other classifiers, whereas statechart diagrams show only statemachines (live statechart diagram example). But feel free to to customize the URL to show exactly what you want to see (you can even mix statechart and class diagram elements in a single diagram). Here are all the supported options so far:

  • showAssociationEndOwnership (boolean)
  • showAssociationEndMultiplicity (boolean)
  • showAssociationName (boolean)
  • showAssociationEndName (boolean)
  • showAttributes (boolean)
  • showClasses (boolean)
  • showClassifierCompartmentForPackage (Current, Immediate, Any)
  • showClassifierStereotypes (boolean)
  • showComments (boolean)
  • showDataTypes (boolean)
  • showElementsInOtherPackage (Never, Immediate, Always)
  • showEmptyClassifierCompartments (NotEmpty, Never, Always)
  • showEnumerations (boolean)
  • showFeatureStereotypes (boolean)
  • showInterfaces (boolean)
  • showOperations (boolean)
  • showParameters (boolean)
  • showParameterDirection (boolean)
  • showParameterNames (boolean)
  • showPrimitives (boolean)
  • showRelationshipStereotypes (boolean)
  • showReturnParameter (boolean)
  • showSignals (boolean)
  • showStateMachines (boolean)
  • showStructuralFeatureVisibility (boolean)

If you feel like you need more control, feel free to enter an issue.

To finish this post, a couple of snapshots for posterity:

Statechart for the Expense entity

Statechart for the Expense entity

Two statecharts from the carserv application

Two statecharts from the carserv application

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Command Query Separation in TextUML

Ever heard of Command Query Separation? It was introduced by Bertrand Meyer and implemented in Eiffel. But I will let Martin Fowler explain:

The term ‘command query separation’ was coined by Bertrand Meyer in his book “Object Oriented Software Construction” – a book that is one of the most influential OO books during the early days of OO. [...]

The fundamental idea is that we should divide an object’s methods into two sharply separated categories:

  • Queries: Return a result and do not change the observable state of the system (are free of side effects).
  • Commands: Change the state of a system but do not return a value.

Query operations in UML

UML too allows an operation to be marked as a query. The section on Operations in the UML specification states:

If the isQuery property is true, an invocation of the Operation shall not modify the state of the instance or any other element in the model.

Query operations in TextUML

The next release of TextUML (which runs in Cloudfier today) will start exposing the ability to mark an operation as a query operation. Being just a notation for UML, the same definition of the UML spec applies to TextUML operations marked as queries.

But how do you mark an operation as a query in TextUML, you ask? You use the query keyword instead of the usual operation keyword (it is not just a modifier, it is a replacement for the usual keyword):

query totalExpenses(toSum : Expense[*]) : Double;

The TextUML compiler imposes a few rules when it sees a query operation:

  • it will require the operation to have a return value
  • it won’t let the operation perform any actions that could have side effects, such as creating or destroying objects, writing properties or linking objects, or invoke any other non-query operations
  • also, it will only let you invoke operations from a property derivation if they are query operations

Example of a query operation


    private query totalExpenses(toSum : Expense[*]) : Double;
    begin
        return (toSum.reduce((e : Expense, sum : Double) : Double {
            sum + e.amount
        }, 0) as Double);
    end;

Example of a derived attribute using a query operation


    derived attribute totalRecorded : Double := {
        self.totalExpenses(self.recordedExpenses)
    };

But why is Command Query Separation a good thing?

By allowing a modeler/programmer to explicitly state whether an operation has side effects allows a compiler or runtime to take advantage of the guarantee of lack of side effects to do things such as reorder invocations, cache results, safely reissue in case of failure which can improve performance and reliability.

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Checking the current state of a UML state machine

In Cloudfier, we use UML as the core language for building business applications. UML is usually well-equipped for general purpose business domain-centric application modeling, but that doesn’t mean it always does everything needed out of the box.

Case at hand: assuming one is developing an expense reporting application and modeled an expense’s status as a state machine (in TextUML):

class Expense
    /* ... */
    attribute status : Status;
    operation review();
    operation approve();
    operation reject();
    operation submit();

    statemachine Status
        initial state Draft
            transition on call(submit) to Submitted;
        state Submitted
            transition on call(approve) to Approved
            transition on call(reject) to Rejected
            transition on call(review) to Draft;
        terminate state Approved;
        terminate state Rejected;        
    end;
end;

How do you model the following in UML?

Show me all expenses that are waiting for approval.

Turns out there is no support in UML for reasoning based on the current state of a state machine.

Creative modeling

So, what do you do when UML does not have a language element that you need? You extend it, in our case, using a stereotype applicable to the LiteralNull metaclass (in TextUML):

stereotype VertexLiteral extends LiteralNull
    property vertex : Vertex;
end;

So, a vertex literal is a value specification, more specifically, a variant of LiteralNull, that can refer to a Vertex, which is a metaclass that represents the states (including pseudo-states) in a state machine.

Notation, notation

In terms of notation, I chose to make State/Vertex literals look like enumeration literals: Status#Approved or Status#Draft. So, back to the original question, this is how you could model a query that returns all expenses that are in the Submitted state:

    static operation findAllSubmitted() : Expense[*];
    begin 
        return Expense extent.select ((e : Expense) : Boolean {
            return e.status == Status#Submitted
        });
    end;

If you are thinking to yourself: I didn’t know UML had queries or closures!?, well, it usually doesn’t. See the posts on SQL queries in UML and Closures in UML for some background on this.

Note also that if you wanted to refer to the symbol Status from a class different than the one enclosing it you will need to qualify it (i.e. Expense::Status#Submitted).

Show me more!

You can run the Expenses application showing state machines and state-based queries in Cloudfier right now (login is “guest” or any of the employee names you will see later).

The entire Expenses sample application (currently 150 lines of generously spaced TextUML) is available on BitBucket. You can also easily check it out into Cloudfier so you can run your own copy of the application on the web (there is nothing to install). Give it a try!

What do you think?

Your feedback (questions, support or criticism) to any of the ideas presented in this post is very welcome.

UPDATE: I started a thread on the subject on the UML Forum group, and turns out you can do this kind of reasoning in OCL, but indeed, not in UML itself. Well, now you can.

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Adding State Machines to TextUML and AlphaSimple [take 1]

I decided to go ahead and finally implement support for state machines in TextUML and AlphaSimple.

This is an example of what a state machine will look like (take 1), based on fig. 15.33 in the UML specification 2.4:


(...)
statemachine Phone

  initial state
    entry { self.startDialTone() }
    exit { self.stopDialTone() }
    transition on digit to PartialDial;

  state PartialDial
    transition on digit to PartialDial
    transition when { self.numberIsValid() } to Completed;

  final state Completed;

end;
(...)

A state machine may declare multiple states. Each state declares a number of transitions to other states. Each transition may be triggered by many events (or none), each denoted by the keyword ‘on’, and may optionally present a guard constraint (using the keyword ‘when’). The initial state is the only one that may remain unnamed. The final state cannot have outgoing transitions, but just like any other state, it may declare entry/exit behaviors.

What do you think? I did try to find existing textual notations for UML, like this and this, but none of those seem to be documented or look like covering all the UML features I want to support. Any other pointers?

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Feedback wanted: invariant constraints in AlphaSimple/TextUML

I am working on support for invariant constraints in AlphaSimple/TextUML.

Some of the basic support has already made into the live site. For instance, the AlphaSimple project has a rule that says:

A user may not have more than 3 private projects.”

This in TextUML looks like this:


class User 

    attribute projects : Project[*] 
        invariant Maximum 3 private projects { 
            return self.privateProjects.size() <= 3
        };
        
    derived attribute privateProjects : Project[*] := () : Project[*] {
        return self.projects.select((p : Project) : Boolean {
            return not p.shared
        });
    };

end;

(Note the constraint relies on a derived property for more easily expressing the concept of private projects, and that backslashes are used to escape characters that otherwise would not be allowed in identifiers, such as whitespaces.)

What do you think? Does it make sense? I know the syntax for higher order functions could benefit from some sugar, but that can be easily fixed later. I am much more interested in feedback on the idea of modeling with executable constraints than in syntax.

Wading in unknown waters

I am in the process of modeling a real world application in AlphaSimple and for most cases, the level of support for constraints that we are building seems to be sufficient and straightforward to apply.

I have though found one kind of constraint that is hard to model (remember, AlphaSimple is a tool for modeling business domains, not a programming language): in general terms, you cannot modify or delete an object if the object (or a related object) is in some state. For example:

"One cannot delete a project's files if the project is currently shared".

Can you think of a feature in UML that could be used to address a rule like that? I can't think of anything obvious (ChangeEvent looks relevant at a first glance, but there is no support for events in TextUML yet).

Any ideas are really appreciated.

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

MDD meets TDD (part II): Code Generation

Here at Abstratt we are big believers of model-driven development and automated testing. I wrote here a couple of months ago about how one could represent requirements as test cases for executable models, or test-driven modeling. But another very interesting interaction between the model-driven and test-driven approaches is test-driven code generation.

You may have seen our plan for testing code generation before. We are glad to report that that plan has materialized and code generation tests are now supported in AlphaSimple. Follow the steps below for a quick tour over this cool new feature!

Create a project in AlphaSimple

First, you will need a model so you can generate code from. Create a project in AlphaSimple and a simple model.


package person;

enumeration Gender 
  Male, Female
end; 

class Person
    attribute name : String; 
    attribute gender : Gender; 
end;

end.

Enable code generation and automated testing

Create a mdd.properties file in your project to set it up for code generation and automated testing:


# declares the code generation engine
mdd.target.engine=stringtemplate

# imports existing POJO generation template projects
mdd.importedProjects=http://cloudfier.com/alphasimple/mdd/publisher/rafael-800/,http://cloudfier.com/alphasimple/mdd/publisher/rafael-548/

# declares a code generation test suite in the project
mdd.target.my_tests.template=my_tests.stg
mdd.target.my_tests.testing=true

# enables automated tests (model and templates)
mdd.enableTests=true

Write a code generation test suite

A code generation test suite has the form of a template group file (extension .stg) configured as a test template (already done in the mdd.properties above).

Create a template group file named my_tests.stg (because that is the name we declared in mdd.properties), with the following contents:


group my_tests : pojo_struct;

actual_pojo_enumeration(element, elementName = "person::Gender") ::= "<element:pojoEnumeration()>"

expected_pojo_enumeration() ::= <<
enum Gender {
    Male, Female
}
>>

A code generation test case is defined as a pair of templates: one that produces the expected contents, and another that produces the actual contents. Their names must be expected_<name> and actual_<name>. That pair of templates in the test suite above form a test case named “pojo_enumeration”, which unsurprisingly exercises generation of enumerations in Java. pojo_enumeration is a pre-existing template defined in the “Codegen – POJO templates” project, and that is why we have a couple of projects imported in the mdd.properties file, and that is why we declare our template suite as an extension of the pojo_struct template group. In the typical scenario, though, you may would have the templates being tested and the template tests in the same project.

Fix the test failures

If you followed the instructions up to here, you should be seeing a build error like this:



Line	File		Description
3	my_tests.stg	Test "pojo_enumeration" failed: [-public -]enum Gender {n Male, Femalen}

which is reporting the code generated is not exactly what was expected – the template generated the enumeration with an explicit public modifier, and your test case did not expect that. Turns out that in this case, the generated code is correct, and the test case is actually incorrect. Fix that by ensuring the expected contents also have the public modifier (note that spaces, newlines and tabs are significant and can cause a test to fail). Save and notice how the build failure goes away.

That is it!

That simple. We built this feature because otherwise crafting templates that can generate code from executable models is really hard to get right. We live by it, and hope you like it too. That is how we got the spanking new version of the POJO target platform to work (see post describing it and the actual project) – we actually wrote the test cases first before writing the templates, and wrote new test cases whenever we found a bug – in the true spirit of test-driven code generation.

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Can you tell this is 100% generated code?

Can you tell this code was fully generated from a UML model?

This is all live in AlphaSimple – every time you hit those URLs the code is being regenerated on the fly. If you are curious, the UML model is available in full in the TextUML’s textual notation, as well as in the conventional graphical notation. For looking at the entire project, including the code generation templates, check out the corresponding AlphaSimple project.

Preconditions

Operation preconditions impose rules on the target object state or the invocation parameters. For instance, for making a deposit, the amount must be a positive value:


operation deposit(amount : Double);
precondition (amount) { return amount > 0 }
begin
    ...
end;

which in Java could materialize like this:


public void deposit(Double amount) {
    assert amount > 0;
    ...
}

Not related to preconditions, another case assertions can be automatically generated is if a property is required (lowerBound > 0):


public void setNumber(String number) {
    assert number != null;
    ...
}

Imperative behavior

In order to achieve 100% code generation, models must specify not only structural aspects, but also behavior (i.e. they must be executable). For example, the massAdjust class operation in the model is defined like this:


static operation massAdjust(rate : Double);
begin
    Account extent.forEach((a : Account) { 
        a.deposit(a.balance*rate) 
    });
end;

which in Java results in code like this:


public static void massAdjust(Double rate) {
    for (Account a : Account.allInstances()) {
        a.deposit(a.getBalance() * rate);
    };
}

Derived properties

Another important need for full code generation is proper support for derived properties (a.k.a. calculated fields). For example, see the Account.inGoodStanding derived attribute below:


derived attribute inGoodStanding : Boolean := () : Boolean { 
    return self.balance >= 0 
};

which results in the following Java code:


public Boolean isInGoodStanding() {
    return this.getBalance() >= 0;
}

Set processing with higher-order functions

Any information management application will require a lot of manipulation of sets of objects. Such sets originate from class extents (akin to “#allInstances” for you Smalltalk heads) or association traversals. For that, TextUML supports the higher-order functions select (filter), collect (map) and reduce (fold), in addition to forEach already shown earlier. For example, the following method returns the best customers, or customers with account balances above a threshold:


static operation bestCustomers(threshold : Double) : Person[*];
begin
    return
        (Account extent
            .select((a:Account) : Boolean { return a.balance >= threshold })
            .collect((a:Account) : Person { return a->owner }) as Person);
end;        

which even though Java does not yet support higher-order functions, results in the following code:


public static Set<Person> bestCustomers(Double threshold) {
    Set<Person> result = new HashSet<Person>();
    for (Account a : Account.allInstances()) {
        if (a.getBalance() >= threshold) {
            Person mapped = a.getOwner();
            result.add(mapped);
        }
    }
    return result;
}

which demonstrates the power of select and collect. For an example of reduce, look no further than the Person.totalWorth attribute:


derived attribute totalWorth : Double := () : Double {
    return (self<-PersonAccounts->accounts.reduce(
        (a : Account, partial : Double) : Double { return partial + a.balance }, 0
    ) as Double);
};  

which (hopefully unsurprisingly) maps to the following Java code:


public Double getTotalWorth() {
    Double partial;
    partial = 0;
    for (Account a : this.getAccounts()) {
        partial = partial + a.getBalance();
    }
    return partial;
}

Would you hire AlphaSimple?

Would you hire a developer if they wrote Java code like AlphaSimple produces? For one thing, you can’t complain about the guy not being consistent. :) Do you think the code AlphaSimple produces needs improvement? Where?

Want to try by yourself?

There are still some bugs in the code generation that we need to fix, but overall the “POJO” target platform is working quite well. If you would like to try by yourself, create an account in AlphaSimple and to make things easier, clone a public project that has code generation enabled (like the “AlphaSimple” project).

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Interview on perspectives on MDD and UML

I had the honor of being interviewed by Todd Humphries, Software Engineer at Objektum Solutions, on my views on UML and model-driven development. Here is an excerpt of the interview:

Todd Humphries: Did you have a ‘Eureka!’ moment when modelling made sense for the first time and just became obvious or was there one particular time you can think of where your opinion changed?

Rafael Chaves: When I was first exposed to UML back in school it did feel cool to be able to think about systems at a higher level of abstraction, and be able to communicate your ideas before getting down to the code (we often would just model systems but never actually build them). The value of UML modeling for the purpose of communication was evident, but that was about it. I remember feeling a bit like I was cheating, as drawing diagrams gave me no confidence the plans I was making actually made a lot of sense.

After that, still early in my career, I had the opportunity of working in a team where we were using an in-house code generation tool (first, as many have done, using XML and XSLT, and later, using UML XMI and Velocity templates, also common choices). We would get reams of Java code, EJB configuration files and SQL DDL generated from the designer models, and it did feel a very productive strategy for writing all that code. But the interesting bits (business logic) were still left to be written in Java (using the generation gap pattern). It was much better than writing all that EJB boilerplate code by hand, but it was still cumbersome and there was no true gain in the level of abstraction, as we would model thinking of the code that would be generated – no surprise, as there was no escaping the facts that we would rely on the Java compiler and JUnit tests to figure out whether the model had problems, and in order to write the actual business logic in Java, we had to be very familiar with the code that was generated. So even though I could see the practical utility of modeling by witnessing the productivity gains we obtained, there was a hackish undertone to it, and while it worked, it didn’t feel like solid engineering.

It was only later, when…

Visit The Technical Diaries, Objektum team’s blog, for the full interview. Do you agree with my views? Have your say (here or there).

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

MDD meets TDD: mapping requirements as model test cases

Executable models, as the name implies, are models that are complete and precise enough to be executed. One of the key benefits is that you can evaluate your model very early in the development life cycle. That allows you to ensure the model is generally correct and satisfies the requirements even before you have committed to a particular implementation platform.

One way to perform early validation is to automatically generate a prototype that non-technical stakeholders can play with and (manually) confirm the proposed model does indeed satisfy their needs (like this).

Another less obvious way to benefit from executable models since day one is automated testing.

The requirements

For instance, let’s consider an application that needs to deal with money sums:

  • REQ1: a money sum is associated with a currency
  • REQ2: you can add or subtract two money sums
  • REQ3: you can convert a money sum to another currency given an exchange rate
  • REQ4: you cannot combine money sums with different currencies

The solution

A possible solution for the requirements above could look like this (in TextUML):

package money;

class MixedCurrency
end;

class Money
  attribute amount : Double;
  attribute currency : String;
  
  static operation make(amount : Double, currency : String) : Money;
  begin 
      var m : Money;
      m := new Money;
      m.amount := amount;
      m.currency := currency;
      return m;
  end;
  
  operation add(another : Money) : Money;
  precondition (another) raises MixedCurrency { return self.currency = another.currency }
  begin
      return Money#make(self.amount + another.amount, self.currency);
  end;
  
  operation subtract(another : Money) : Money;
  precondition (another) raises MixedCurrency { return self.currency = another.currency }  
  begin
      return Money#make(self.amount - another.amount, self.currency);      
  end;
  
  operation convert(anotherCurrency : String, exchangeRate : Double) : Money;
  begin
      return Money#make(self.amount * exchangeRate, anotherCurrency);
  end;  
end;
        
end.

Now, did we get it right? I think so, but don’t take my word for it.

The proof

Let’s start from the beginning, and ensure we satisfy REQ1 (a money sum is a pair <amount, currency>:

[Test]
operation testBasic();
begin
    var m1 : Money;
    m1 := Money#make(12, "CHF");
    Assert#assertEquals(12, m1.amount);
    Assert#assertEquals("CHF", m1.currency);
end;

It can’t get any simpler. This test shows that you create a money object providing an amount and a currency.

Now let’s get to REQ2, which is more elaborate – you can add and subtract two money sums:

[Test]
operation testSimpleAddAndSubtract();
begin
    var m1 : Money, m2 : Money, m3 : Money, m4 : Money;
    m1 := Money#make(12, "CHF");
    m2 := Money#make(14, "CHF");

    m3 := m1.add(m2);    
    Assert#assertEquals(26, m3.amount);
    Assert#assertEquals("CHF", m3.currency);
    
    /* if m1 + m2 = m3, then m3 - m2 = m1 */
    m4 := m3.subtract(m2);
    Assert#assertEquals(m1.amount, m4.amount);
    Assert#assertEquals(m1.currency, m4.currency);
end;

We add two values, check the result, them subtract one of them from the result and expect the get the other.

REQ3 is simple as well, and specifies how amounts can be converted across currencies:

[Test]
operation testConversion();
begin
    var m1 : Money, result : Money;
    m1 := Money#make(3, "CHF");
    result := m1.convert("USD", 2.5);
    Assert#assertEquals(7.5, result.amount);
    Assert#assertEquals("USD", result.currency);
end;

We ensure conversion generates a Money object with the right amount and the expected currency.

Finally, REQ4 is not a feature, but a constraint (currencies cannot be mixed), so we need to test for rule violations:

[Test]
operation testMixedCurrency();
begin
    try
        Money#make(12, "CHF").add(Money#make(14, "USD")); 
        /* fail, should never get here */
        Assert#fail("should have failed");
    catch (expected : MixedCurrency)
        /* success */
    end;
end;

We expect the operation to fail due to a violation of a business rule. The business rule is identified by an object of a proper exception type.

There you go. Because we are using executable models, even before we decided what implementation platform we want to target, we already have a solution in which we have a high level of confidence that it addresses the domain-centric functional requirements for the application to be developed.

Can you say “Test-driven modeling”?

Imagine you could encode all non-technical functional requirements for the system in the form of acceptance tests. The tests will run against your models whenever a change (to model or test) occurs. Following the Test-Driven Development approach, you alternate between encoding the next requirement as a test case and enhancing the model to address the latest test added.

Whenever requirements change, you change the corresponding test and you can easily tell how the model must be modified to satisfy the new requirements. If you want to know why some aspect of the solution is the way it is, you change the model and see the affected tests fail. There is your requirement traceability right there.

See it by yourself

Would you like to give the mix of executable modeling and test-driven development a try? Sign up to AlphaSimple now, then open the public project repository and clone the “Test Infected” project (or just view it here).

P.S.: does this example model look familiar? It should – it was borrowed from “Test Infected: Programmers Love Writing Tests“, the classical introduction to unit testing, courtesy of Beck, Gamma et al.

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Productivity of UML

Found an old discussion on the MDSN site about a study on the productivity of UML, brought up by the DSM folks. You can see some of the common caveats raised in this comment by MetaCase’s Steve Kelly. Please read his points and come back here.

I actually didn’t notice it was an old thread and replied to it. Call me cheap, but I hate perfectly good arguments going to waste on a dead thread, so I am recycling my original response (now deleted) here as a blog post.

1) repeat with me, UML is not a graphical language – it has a graphical notation, but others are allowed. Criticism of UML as a whole based on the productivity issues around the graphical notation is cherry picking or (at best) a misinformed opinion. If you don’t like the default notation, create one (like we did!) to suit your taste (and it will still be UML). The specs are public, and there are good open source implementations of the metamodel, that are used by many tools.

2) you don’t need to give up on the semantics of UML to map a modeled class to multiple artifacts. That is just plain OO design mapping to real-world implementation technologies. UML is an OO language first and foremost.

3) There is no need to mix languages, UML has support for both structural and behavioral modeling (since 2002!). Action languages are not (or don’t have to be) “other languages” – but just a textual notation on top of the existing abstract syntax and semantics. That is not a marketing ploy, incorporating elements of the Shlaer-Mellor approach was just a sound strategic decision that made UML much better.

4) Annotations (or stereotypes) is an established (see C#, Java) and cost effective way of tailoring a general purpose language to one’s needs. Not everything calls for a DSL. Both approaches have pros and cons, one has to pick what is best for the situation at hand.

5) All the stories of failure or limited success with generating code from UML models I heard or read are caused by the decision of ignoring behavioral modeling in UML and doing partial code generation. That is a losing proposition, no matter the modeling language. Again, just like the notation issue, analyzing UML productivity based exclusively on those narrow minded cases is at best spreading misinformation. Kudos to MetaCase for promoting full code generation, that is the way to go. But full code generation is not an exclusivity of DSL, the Executable UML folk (and other modeling communities) have been doing it successfully for a long time as well.

Can we move away from the pissing contest between modeling approaches? That got old ages ago. There are way more commonalities than differences between DSM and executable modeling with GPLs like UML, productivity gains included. There is room for both approaches, and it would not be wise to limit oneself to one or another.

What is your opinion? Are you still using old school UML and limiting yourself to generating stubs? Why on earth haven’t you moved to the new world of executable models yet?

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Generating code from UML models using AlphaSimple and StringTemplate

We just released a new build of AlphaSimple with basic support for customized code generation using templates, more specifically, using StringTemplate templates. Let’s take a quick tour:

Create a model in AlphaSimple

Create your model in AlphaSimple. If you need help with the notation, check the tutorial. You can just start with the default contents you get when creating a new project:

package NewProject;

class NewProject
    /* attributes */
    attribute text : String;
    attribute check : Boolean;
    attribute number : Integer[0,1];
    attribute date : Date[0,1];
    /* relationships */
    /* actions */
    operation toggle();
    begin
        self.check := not self.check;
    end;
    /* queries */
end;

end.

Remember to save your file.

Create a template

AlphaSimple supports StringTemplate as template language (check out this 5-minute introduction). In order to define a template in your AlphaSimple project, create a file with the .stg extension (in StringTemplate lingo, it is a template group file). You can use the example below, which for every class in a model, creates a text file that shows its name, and the names of its attributes and operations:

group simple;

outputPath(class) ::= <<
<! The 'outputPath' template is optional and determines the path of the file generated (the default is the class name) !>
<class.name>.txt
>>

contents(class) ::= <<
<! The 'contents' template is mandatory and is the entry point for generating the contents of the file from a class. !>
Class: <class.name>

Attributes: <class.ownedAttributes:{attr|<attr.name>};separator=", ">

Operations: <class.ownedOperations:{op|<op.name>};separator=", ">
>>

Again, remember to save this file.

Declare your custom template

To enable custom templates, you need to create an AlphaSimple configuration file (mdd.properties). It is a configuration file that drives the compiler and code generation in AlphaSimple. Your file can be as simple as this:

# the template implementation we use
mdd.target.engine=stringtemplate

# the templates supported (always mdd.target.<name>.template=<template file name>
mdd.target.simple.template=simple.stg

Both entries are mandatory. Ensure the line declaring the template matches the name you chose when creating the template file. Save this file.

Test your template

In order to test your custom template, if you have been using a guest account, you will need to sign up first (it’s free). Your project contents will be preserved.

First, publish your project (see button in the editor). Then, from your list of projects (“My Projects”), share your project (open lock button). For any future modifications to model, template or configuration file, you will need to publish your changes again. This will not be required in the future.

We are almost there. Since there is no UI for triggering custom generation yet, you will need to use the REST API, which is quite easy. Find out the numeric id of your project (from any link pointing to it). Then hit a URI with this shape:

http://cloudfier.com/alphasimple/mdd/publisher/<username>-<project-id>/

For instance, for project 515, belonging to user simple, the URI would be:

http://cloudfier.com/alphasimple/mdd/publisher/simple-515/

which returns:

<workspace packageCount='1' timestamp='1298449947000'>
  <model name='NewProject.uml'
    uri='http://cloudfier.com/alphasimple/mdd/publisher/simple-515/NewProject.uml?secret='
    graph='http://cloudfier.com/alphasimple/mdd/diagram/simple-515/NewProject.uml?secret='/>
  <properties name='mdd.properties' uri='http://cloudfier.com/alphasimple/mdd/publisher/simple-515/mdd.properties?secret='/>
  <source name='NewProject' uri='http://cloudfier.com/alphasimple/mdd/publisher/simple-515/NewProject?secret='/>
  <source name='simple.stg' uri='http://cloudfier.com/alphasimple/mdd/publisher/simple-515/simple.stg?secret='/>
  <generator platform="jpa" uri="http://cloudfier.com/alphasimple/mdd/generator/simple-515/jpa?secret="/>
  <generator platform="pojo" uri="http://cloudfier.com/alphasimple/mdd/generator/simple-515/pojo?secret="/>
  <generator platform="simple" uri="http://cloudfier.com/alphasimple/mdd/generator/simple-515/simple?secret="/>
  <renderer uri="http://cloudfier.com/alphasimple/animator/index.jsp?repository=simple-515#"/>
</workspace>

which gives you access to all the objects that AlphaSimple project has: source files (model and template), the configuration file, the generated UML model and corresponding class diagram, and, what we are mostly interested here, all generators available. Note that it includes not only a generator for the custom template, but some other built-in generators as well. But lets ignore those for now, and open the generator URI for our custom template (named “simple”). Voila, this is what you should see:

Class: NewProject

Attributes: text, check, number, date

Operations: toggle

Conclusion

We hope this very simple example gave you an idea of how you can generate code from UML models using AlphaSimple and StringTemplate (even if it doesn’t really generate actual code). In the example template, we only navigate from a class to its operations and attributes, and access their names, but your template has virtually any information from the underlying UML model available to generate from.

If you would like to see more interesting models and actual code generation templates, browse the shared project area. For now, there is currently just one project with an elaborate template. Clone it and model (and generate) away. If you have any feedback, just post a comment here or check the AlphaSimple contact page.

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Model-driven Development with Executable UML models

Last November I did a lecture on Model-driven Development with Executable UML models to a class of Software Engineering undergrad students at UVic. Here are the slides:

I think it gives a good summary of my views on model driven development (with Executable UML or not):

  • even though problem domains are typically not very complex, enterprise software is complex due to the abundance of secondary crosscutting concerns (persistence, concurrency, security, transactions etc)
  • there are two dominant dimensions in enterprise software: business domain concerns and technological concerns
  • they are completely different in nature (change rate, abstraction level) and require different approaches (tools, skills, reuse)
  • MDD is a strategy that handles well that divide: models address business domain concerns, PIM->PSM transformation addresses technological concerns
  • brainstorming, communication, documentation and understanding (rev. engineering) are not primary goals of MDD – to produce running code in a productive and rational way is
  • models in MDD must be well-formed, precise, complete, executable, technology independent
  • graphical representations are not suitable for executable modeling (textual notations are much better)
  • diagrams != models, text != code (that would look good on a t-shirt!)

I guess those who know me won’t have seen anything new above (these ideas make the very foundations of the TextUML Toolkit and AlphaSimple).

Do you agree with those positions?

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

The value of UML (or lack thereof) in the development process

[What follows was adapted from a discussion on LinkedIn on why companies developing software don't use UML more]

UML proponents argue that by using UML one gains the ability to properly represent important knowledge on the problem domain and intended solution design. That leads to good things such as improved communication and better quality of the software.

I certainly don’t question that. The ability of devising a solution at the right level of abstraction is extremely important. Yet, that on its own is not enough. It doesn’t help if the language that allows you to specify a solution at the right level of abstraction does not lead to running code (via code generation or direct execution). If one has to specify a solution again by implementing by hand (say, in Java or C#) what the model describes (i.e. using the model as a reference), that greatly offsets any gains from modeling. As the agilists say: Don’t Repeat Yourself. I won’t bore you with the consequences of breaking that simple rule, it must be obvious to any developer worth their salt.

If you are going to be pragmatic, and you cannot generate running code from your models, from a developer’s point of view, there is no much value for UML in the development process.

Developers play an increasingly important role in software product development. Non-executable UML models are seen as fluff, an unnecessary burden to software developers, and hence the poor reputation with that crowd, as the majority of the places using UML is unfortunately using it that way.

I see two solutions for this: either get models to be executable, or get programming languages to support a higher level of abstraction. Even though I am a believer of the former (and our work at Abstratt follows that approach – see AlphaSimple and TextUML Toolkit), I won’t be surprised if the latter ends up being the winning approach (see RAD frameworks such as Grails and Rails).

Do you agree? Which approach do you prefer? Why?

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Take control over UML class diagrams in AlphaSimple

As recently announced, shared models in AlphaSimple now sport UML class diagrams thanks to Graphviz support in the Google Charts API .

What I didn’t mention is that you can customize how diagrams are rendered by specifying query parameters on the image URL. For instance, compare the basic diagram from the previous post with the customized diagram below (click on it to see the URL). Can you spot the differences? :)

Here are all supported options:

  • showAssociationEndOwnership (boolean)
  • showStructuralFeatureVisibility (boolean)
  • showAssociationEndMultiplicity (boolean)
  • showAssociationName (boolean)
  • showAssociationEndName (boolean)
  • showClassifierCompartmentForPackage (Current, Immediate, Any)
  • showClassifierStereotypes (boolean)
  • showElementsInOtherPackage (Never, Immediate, Always)
  • showEmptyClassifierCompartments (NotEmpty, Never, Always)
  • showFeatureStereotypes (boolean)
  • showParameterDirection (boolean)
  • showPrimitives (boolean)
  • showRelationshipStereotypes (boolean)

Give it a try (don’t forget you need to share your projects, and republish for changes to become visible to others). Are there more control options you would like to see?

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

UML may suck, but is there anything better?

UML has been getting a lot of criticism from all sides, even from the modeling community. Sure, it has its warts:

  • it is a huge language, that wants to be all things to all kinds of people (business analysts, designers, developers, users)
  • it has a specification that is lengthy, hard to navigate and often vague, incomplete or inconsistent
  • it is modular, but its composition mechanism (package merging) is esoteric and not well understood by most
  • it is extensible, but language extensions (profiles and stereotypes) are 2nd-class citizens
  • it lacks a reference implementation
  • its model interchange specification is so vague that often two valid implementations won’t work with each other
  • its committees work behind closed doors, there is no opportunity for non-members to provide feedback on specifications while they are in progress (membership is paid)
  • <add your own grudges here>

However, even though I see a lot of room for improvement, I still don’t think there is anything better out there. The more I become familiar with the UML specification, the more impressed I am about its completeness, and how issues I had never thought about before were dealt with by its designers. And it seems that the OMG recognizes some of the issues I raised above as shortcomings and is working towards addressing them. Unfortunately, some fundamental problems are likely to remain.

In my opinion (hey, this is my blog!), for a modeling language to beat UML:

  • it must be general purpose, not tailored to a specific architecture or style of software
  • it must not be tailored to an implementation language
  • it must be based on or compatible with the object paradigm
  • it must not be limited to one of the dominant aspects of software (state, structure, behavior)
  • it must be focused on executability/code generation (and thus suitable for MDD) as opposed to documentation/communication
  • it must be modular, and user extensions should be 1st class citizens
  • its specification should follow an open process
  • it must not be owned/controlled by a single company
  • it must not require royalties for adoption/implementation

My suspicion is that the next modeling language that will beat the UML as we know today is the future major release of UML. Honestly, I would rather see a new modeling language built from scratch, focused on building systems, that didn’t carry all that requirement/communication/documentation-oriented crap^H^H^H^Hbaggage that UML has (yes, I am talking about you, use case, sequence, instance and collaboration diagrams!), and developed in a more open and agile process than the OMG can possibly do. But I am not hopeful. The current divide between general purpose and domain specific modeling communities is not helping either.

So, what is your opinion? Do you think there are any better alternatives that address the shortcomings of UML without imposing any significant caveats of their own? Have your say.

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Doing away with custom UML metaclasses in TextUML

The TextUML Toolkit has since release 1.2 had a metamodel extension package (inaptly named ‘meta’). This metamodel extension package defined new metaclasses not available in UML such as:

  • closure – an activity that has another activity as context
  • conversion action – an action that flows an input directly as output just changing the type
  • literal double – a literal value for double precision numeric values
  • signature – a classifier that contained parameters, the type of a closure
  • meta value specification – a value specification for meta references
  • collection literals – a value specification that aggregates other value specifications

Turns out extending the UML metamodel by definining new packages and metaclasses is a bad idea. Some reasons (yes, I am feeling ‘bullety’):

  • it is non-standard
  • other UML tools cannot read instances of your metaclasses, some won’t read your models at all if they have *any* unknown metaclasses
  • there is little documentation on how to maintain these kinds of metamodel extensions
  • since it is not the mainstream approach, we are bound to encounter more issues

Because of that, release 1.6 will rely exclusively on profiles and stereotypes for extending the UML metamodel.

What to expect

For conventional users of the Toolkit, this change might possibly go unnoticed, barring any potential bugs introduced in the process.

People using the built-in base package and the base_profile will be directly affected. The elements in these models are still provided, by they are now in the new mdd_extensions profile, or one of the new mdd_types, mdd_collections and mdd_console packages.

We apologize for any inconvenience these changes might bring, but we strongly believe they are required to make the TextUML Toolkit a better product. If you have any trouble moving to 1.6 (to be released later this month), make sure to hit the user forums or report issues.

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Can TextUML be implemented the generative way (with Xtext or EMFText)?

I have been trying to figure out whether the TextUML notation for action semantics can be dealt with properly by tools such as Xtext and EMFText (class models and state machines should be fine). For example, given this structural model fragment:

class Advertisement 
    attribute summary : String;
    attribute description : String; 
    attribute keywords : Keyword[*]; 
    attribute category : Category;  
    operation addKeyword(keywordName : String);
    static operation findByCategoryName(catName : String) : Advertisement[*];
end;

association AdvertisementKeyword
    role Advertisement.keywords;
    role advertisement : Advertisement;
end;

class Keyword specializes Object
    attribute name : String;
end;

class Category specializes Object
    attribute name : String;
    attribute description : String;
end;

association AdvertisementCategory
    role Advertisement.category;
    role ad : Advertisement[*];
end;

Notice the Advertisement class declares two operations. Their behaviour in TextUML could be written as:

    operation Advertisement.addKeyword;
    begin
        var newKeyword : Keyword; 
        newKeyword := new Keyword;
        newKeyword.name := keywordName;
        link AdvertisementKeyword(keywords := newKeyword, advertisement := self);
    end;

    operation Advertisement.findByCategoryName;
    begin  
        return Advertisement extent.select(
            (a : Advertisement) : Boolean {
                return a->AdvertisementCategory->category.name = catName; 
            }
        );
    end;

Note that TextUML allows the behavior to be specified inline when declaring an operation in a class, or in separate, as above (that explains the lack of parameters, modifiers etc).

In the resulting UML model, the behaviour of Advertisement.addKeyword would roughly map to this (using a textual pseudo-notation for UML activities that is hopefully more readable than raw XMI):

activity(name: "addKeyword") {
    structuredActivityNode {
        variable(name: "newKeyword", type: #String)
        writeVariable(variable: #newKeyword, value: createObject(class: #Keyword))
        writeAttribute(
            attribute: #Keyword.name, 
            target: readVariable(variable: #newKeyword), 
            value: readVariable(variable: #keywordName)
        )
        createLink(
            association: #AdvertisementKeyword, 
            end1: #AdvertisementKeyword.keyword, 
            end1Value: readVariable(variable: #newKeyword), 
            end2: #AdvertisementKeyword.advertisement, 
            end2Value: readSelf()
        )
    }
}

and the behaviour Advertisement.findByCategoryName would map to this:

activity(name: "findByCategoryName") {
    structuredActivityNode {
        // implicit variable for return value
        variable(name: "@result", type: #Advertisement, upperBound: *)
        // implicit variable for parameter value
        variable(name: "catName", type: #String)
        writeVariable(
            variable: #@result, 
            value: callOperation(
                operation: #Advertisement.select, 
                target: readExtent(class: #Advertisement),
                filter: metaValue(#@findByCategoryName_closure1)
            )
        )
    }
}

// a closure is an activity that has a reference to a context activity
closure(name: "@findByCategoryName_closure1") {
        // implicit variable for return value
        variable(name: "@result", type: #Boolean)
        // implicit variable for parameter value
        variable(name: "a", type: #Advertisement)
        writeVariable(
            variable: #@result, 
            value: callOperation(
                operation: #Object.equals,
                // variables from the context activity are available here
                target: readVariable(variable: #catName)
                args: readAttribute(attribute: #Category.name, target: readLink(
                    association: #AdvertisementCategory,
                    fedEnd: #Advertisement.ad,
                    fedEndValue: readVariable(variable: #a),
                    readEnd: #Advertisement.category
                ))
            )          
        )
}

Note that UML does not have closures, this is an extension to the UML metamodel which I wrote about here before.

Some background on the metaclasses involved: ReadVariableAction, CreateObjectAction, CreateLinkAction, ReadExtentAction etc are all action metaclasses. Actions are the building blocks for modeling activity behaviour in UML.

The million dollar question is: can Xtext and EMFText handle more complex textual notations like this? Is this out of the happy path? Has anyone done something similar? I am under the impression I could use Xtext or EMFText better if I used them based on a intermediate metamodel for behavior that would be closer to the concrete syntax (to get all the IDE bells and whistles for free) and then transformed that to UML in a separate step.

If you have the answers for any of these questions (or even if you have comments and questions of your own), please chime in.

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

New in 1.3 M1: better integration with diagramming tools

Even though it has always been possible to open models generated by the TextUML Toolkit in UML2-based diagramming tools, until 1.2 the Toolkit assigned new ids to each element every time a model was regenerated. That meant that any diagrams based on the model being regenerated would become invalid as any cross-references from the diagram to the model would be broken.

Starting with 1.3 M1 (test build available from the milestones update site), the TextUML Toolkit is now better compatible with diagramming tools based on UML2. You can edit the source in TextUML and save it to cause model generation to occur, and any existing diagrams should still remain valid.

Here you can see the TextUML Toolkit being used side-by-side with the UML2 Tools graphical editor:

Actually, I found out that UML2 Tools can be a great companion for the TextUML Toolkit because it seems to just do the right thing when it notices the underlying model has been changed externally: new elements show up, removed elements disappear, preserved elements preserve their layout. I tried doing the same with Papyrus 1.11 and unfortunately it does not seem to handle external updates properly. I haven’t tried with other UML2-based diagram editors, so at this point I am not sure which one represents the trend here.

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

SQL queries in UML

I strongly believe queries are an essential part of a domain model. As such, in our quest to have (UML) models that can fully (yet abstractly) describe object models for the common enterprise applications, we cannot leave out first class support for queries.

But how do you do queries in UML? The obvious answer seems to be OCL, but that is not the approach I am taking as OCL and UML have serious interoperability/duplication issues. Instead, I took theĀ  middleweight extension approach.

First, we model a protocol for manipulating collections of objects (showing only a subset here):

class Collection specializes Basic
  operation includes(object : T) : Boolean;
  operation isEmpty() : Boolean;
  operation size() : Integer;
  operation exists(predicate : {(:T) : Boolean}) : Boolean;
  operation any(predicate : {(:T) : Boolean}) : T;
  operation select(filter : {(:T) : Boolean}) : T[*];
  operation collect(mapping : {(:T) : any}) : any[*];
  operation forEach(predicate : {(:T)});
  operation union(another : T[*]) : T[*];
  (...)
end;

That protocol is available against any collection of objects, which in UML can be obtained by navigating an association, reading an attribute, invoking an operation, obtaining the extent of a class (remember Smalltalk’s allInstances), anything where the resulting value has multiplicity greater than one.

Note most of the operations in the Collection protocol take blocks/closures as arguments. Closures are used in this context to define the filtering criterion for a select, or the mapping function for a collect.

For instance, for obtaining all accounts that currently do not have sufficient funds, this method would do it:

static operation findNSFAccounts() : Account[*];
begin
    return Account extent.select(
        (a : Account) : Boolean {return a.balance < 0}
    );
end;

Note the starting collection is the extent of the Account class. That is very similar to what is done in the context of query languages for object-oriented databases, such as OQL or JDOQL. We then filter the class extent by selecting only those accounts that have a negative balance, by passing a block to the select operation.

When mapping that behavior to SQL, we could end up with a query like this:

select _account_.* from Account _account_ where _account_.balance < 0

Another example: we want to obtain all customers with a balance above a given amount, let’s say, to send them a letter to thank them for their business. The following method specifies that logic:

static operation findBestCustomers(minBalance : Real) : Customer[*];
begin
    return (Account extent.select(
          (a : Account) : Boolean { return a.balance >= minBalance }
    ).collect(
          (a : Account) : Customer { return a->AccountOwner->owner }
    ) as Customer);
end;

Note that we start off with the extent of Account class, filter it down to the accounts with good balance using select, and then map from that collection to a collection with the respective account owners by traversing an association using collect.

If that was going to be mapped to SQL, one possible mapping would be:

select _customer_.* from Account _account_
    inner join Customer _customer_
        on  _account_._accountID_ = _customer_._customerID_
    where _account_.balance >= ?

Much of this can be already modeled if you try it out with the TextUML Toolkit 1.2. But, you might ask, once you model that, what can you do with UML models containing queries like the ones shown here?

Since the models are complete (include structure and behavior), you can:

  1. Execute them. Imagine writing automated tests against your models, or letting your customer play with them before you actually start working on the implementation.
  2. Generate complete code. The generated code will include even your custom queries, not only those basic ones (findAll, findByPK) code generators can usually produce for you.

If you would like to see tools that support that vision, keep watching this blog.

So, what is your opinion?
Do you see value in being able to specify queries in your models? Is this the right direction? What would you do differently?

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter