AndroMDA (pronounced "Andromeda") is a free and Open Source extensible generator framework that adheres to the Model Driven Architecture (MDA) paradigm. It transforms UML models into deployable components for your favorite platform. While AndroMDA ships with cartridges that can generate code for several platforms and technologies, this tutorial will focus on generating a Microsoft .NET 2.0 application.
The paragraph above might have sounded like a lot of acronyms and marketing speak, but using AndroMDA means one main thing: write less code. Not only that, AndroMDA also lets you create better applications and maintain better order on large projects. By generating as much code as possible, it helps you enforce best practices as well letting developers focus on higher level problems instead of spending time on repetitive plumbing code. Additionally, AndroMDA can generate highly customized enterprise quality code to meet your project's specific needs.
During development of large applications, most architects and developers already create class diagrams and data diagrams. Typically, these diagrams are made in tools like Visio, and the resulting artifacts are static pictures. When code changes, the diagrams must be updated. With AdnroMDA, these diagrams become a living part of your application. They are used to generate large portions of your application, and hence always reflect the current state of the system. When you need to modify your application, you change the model first, regenerate the code, and then add or update custom code as necessary. You get a production quality application out of assets that you had to create anyway.
AndroMDA provides several cartridges out-of-the-box. For example, the NHibernate and NSpring cartridges generate robust service and data layers for your application. In addition, database schema can be exported to script files, allowing the creation of your application's database. There is also an easy way to map your model to an existing schema if your database has already been defined. If you wish to generate custom artifacts from your model, you can write a custom cartridge to accomplish this.
Before you begin
Understanding new tools and technologies can be a daunting task. AndroMDA is no exception. This tutorial serves as a gentle introduction to the power of AndroMDA. We will show you step-by-step how to set up your development environment and build your first .NET application. Instead of mechanically going through a series of steps, we will focus on ideas and concepts. Armed with this knowledge, you will be ready to take on real world challenges. Please set aside half a day of quality uninterrupted time to learn AndroMDA as there is a lot to learn. Then brew a good cup of coffee and immerse yourself in the wonderful world of Model Driven Architecture.
When to use AndroMDA?
AndroMDA is an extremely useful tool, but you should know when to use it.
AndroMDA is a great choice when
- You are starting a new project
- You want to save time by generating as much code as possible
- You are building an application that stores its data in a database
AndroMDA might not be the best choice if
- Your application uses an existing database that cannot be easily mapped to an object model (for example, it does not use primary keys, and object relationships are not clear)
- You already have a mature application with existing business objects, and the incremental effort to model existing components is too high to provide enough value
Now that you understand what AndroMDA is and when you should use it, let's review some foundational concepts that form the basis of modern enterprise applications. After reviewing these concepts, we will discuss how AndroMDA implements them in the applications it generates.
Background - Application architecture
Modern enterprise applications are built using several components connected to one another, each providing a specific functionality. Components that perform similar types of functions are generally grouped into layers. These layers are further organized as a stack in which components in a higher layer use the services of components in the layer below. A component in a given layer will generally use the functionality of other components in its own layer or the layers below it. The diagram below shows a popular layer structure for an enterprise application.
- Presentation Layer: The presentation layer contains components needed to interact with the user of the application. Examples of such components are web pages, rich-client forms, user interaction process components, etc.
- Business Layer: The business layer encapsulates the core business functionality of the application. Simple business functions can be implemented using stateless components whereas complex long running transactions can be implemented using stateful workflows. The business components are generally front-ended by a service interface that acts as a facade to hide the complexity of the business logic. This is commonly known as Service-Oriented Architecture (SOA).
- Data Access Layer: The data access layer provides a simple API for accessing and manipulating data. The components in this layer abstract the semantics of the underlying data access technology, thus allowing the business layer to focus on business logic. Each component typically provides methods to perform Create, Read, Update, and Delete (CRUD) operations for a specific business entity.
- Data Stores: Enterprise applications store their data in one or more data stores. Databases and file systems are two very common types of data stores.
Note that layers are simply logical groupings of components that make up an application. How these layers are deployed on physical machines can vary widely depending on several factors. In the very simplistic scenario, all the layers can reside on one machine. In a slightly more complex scenario, the presentation layer can reside on one machine, the business and data access layers on a second machine, and the database on a third machine. More elaborate scenarios are possible. For example, a high traffic web site can deploy the presentation layer on a web farm consisting of tens of machines.
Architecture of AndroMDA generated applications
Now that we understand the basic concepts behind modern enterprise applications, let's discuss how AndroMDA implements these concepts. AndroMDA takes as its input a business model specified in the Unified Modeling Language (UML), and generates significant portions of the layers needed to build a .NET application. AndroMDA's ability to automatically translate high-level business specifications into production quality code results in significant time savings when implementing .NET applications. The diagram below maps various application layers to .NET technologies supported by AndroMDA.
- Presentation Layer: AndroMDA does not currently offer any generation for the front end, but typically applications are built with either Windows Forms or ASP.NET 2.0.
- Business Layer: The business layer generated by AndroMDA consists primarily of services that are generated from classes defined in the model. These services can be used directly as classes in application code, or they can easily be wrapped in an ASMX Web Service.
- Data Access Layer: AndroMDA leverages the popular object-relational mapping tool called NHibernate to generate the data access layer for applications. It generates data access objects (DAOs) for entities defined in the UML model. These data access objects use the NHibernate API to convert database records to objects, and vice-versa.
- Data Stores: Since AndroMDA generated applications use NHibernate to access data, you can use any of the databases supported by NHibernate.
Data propagation between layers
In addition to the concepts discussed above, it is important to understand how data propagates between various layers of an application. Let us start from the bottom up.
As you know, relational databases store data as records in tables. The data access layers fetch these records from the database, and transform them in to objects that represent entities in the business domain. Hence these objects are called business entities.
The data access layer passes the business entities to the business layer, which performs business logic using these entities.
The last thing to discuss is the propagation of data between the business layer and the presentation layer. There are really two schools of thought on this subject. Some people recommend that the presentation layer should be given direct access to business entities. Others recommend just the opposite, i.e., business entities should be off limits to the presentation layer and that the business layer should package necessary information into so called "value objects" and transfer these value objects to the presentation layer. Let's look at the pros and cons of these two approaches.
The first approach (entities only, no value objects) is simpler to implement. You do not have to create value objects or write any code to transfer information between entities and value objects. In fact, for simple small applications where the the presentation layer and the service layer run on the same machine, this approach will probably work just fine. However, for larger more complex applications, this approach does not scale well. Here's why:
- Business logic is no longer contained in the business layer. It is tempting to freely manipulate entities in the presentation layer and thus spread the business logic in multiple places - definitely a maintenance nightmare. In case there are multiple front-ends to a service, business logic must be duplicated in all these front-ends. In addition, there is no protection against the presentation layer corrupting the entities - intentionally or unintentionally!
- When the presentation layer is running on a different machine (as in the case of a rich client), it is very inefficient to serialize a whole network of entities and send it across the wire. Take the example of showing a list of orders to the user. In this scenario, you really don't need to transfer the gory details of every order to the client application. All you need is perhaps the order number, order date, and the total amount for each order. Later, if the user wishes to see the details of a specific order, you can always serialize that entire order and send it across the wire.
- Passing real entities to the client may pose a security risk. Do you want the client application to have access to the salary information inside the Employee object or your profit margins inside the Order object?
Value objects provide a solution for all these problems. Yes, they require you to write a little extra code, but in return, you get a bullet-proof business layer that communicates efficiently with the presentation layer. You can think of a value objects as a controlled view in to one or more entities that is relevant to your client application. Note that AndroMDA provides some basic support for translation between entities and value objects, as you will see in the tutorial.
Services and NHibernate sessions
Another key concept about AndroMDA generated applications is the strong association between service methods (i.e., operations exposed by a service) and NHibernate sessions. But before we introduce this concept, we must lay out some ground work. The NHibernate session is a runtime object that allows an application to create, read, update, and delete entities in the data store. As long as the session is "open", these entities are attached to the session and you can navigate from one entity to another using relationships between them. If a related entity is not yet in memory, NHibernate will automatically pull it in for you (this is called "lazy loading"). However, as soon you close the NHibernate session, the entities that exist in memory are considered to be "detached", i.e., NHibernate no longer knows about them. You are free to hold references to such entities, but NHibernate will no longer pull in associated entities if they don't exist in memory already. If you accidentally try to access such associated entities, you will get an NHibernate
Now that we understand this background material, let us discuss the relationship between a service method and an NHibernate session. When a client application calls a service method, a new NHibernate session is opened automatically. You do not have to write any code to do this. Similarly, when the service method exits, the associated NHibernate session is closed automatically. In other words, the lifespan of an NHibernate session is bounded by the beginning and ending of a service method call. Consequently, entities are attached to an NHibernate session for the entire duration of the service call, but are detached from this session as soon as the service call ends. As a result, if your service method returns raw entities, the client must be extra careful not to access related entities that are not in memory already. Anyway, you can avoid all this by following the recommendation in the earlier section, i.e., transfer all relevant information into value objects while the session is still open and pass these value objects back to your caller as a return value. In other words, think of a service method as a logical transactional boundary - do everything you need to do within the method, and then return the results as value objects.
Another implication of the strong association between a service method and an NHibernate session is that client applications should not try to bypass the service layer and interact directly with the lower layers. You may be able to brute force your way into one data access object, but sooner or later, you will get into trouble!
Now that you understand the basic tenets of AndroMDA, let's describe the sample application we will implement in this tutorial.
Please continue to part 2.