Home  >  

Getting Real with LCDS 3, Part 1

Author photo
AddThis Social Bookmark Button

Enterprise RIAs are big, and not the "big-in-Japan" kind of big, but big as in I use one every day. New apps are born almost daily and buzz across the blogosphere. It's usually dashboard-this and data-viz-that, but every once in a while we get an amazing look-ma-I-made-a-heat-map. Flex frontends power many of the richest RIA's, and Adobe's LiveCycle Data Services has always been known as a robust enterprise backend. But the forthcoming LCDS 3 is something different and something wonderful. To me, LCDS 3 changes the game, and I fully expect the richest of the next generation enterprise RIAs to have LCDS 3 under the hood. In the quest to ease the pain of enterprise development and empower the developer, Adobe has turned to Model Driven Development. Adobe's vision for MDD can be succinctly described as a point-and-click tool conveniently packaged as a Flash Builder 4 (aka Eclipse) plugin that can be seamlessly married to an LCDS backend. The software is still beta, so beware of the occasional bug, but for me the future is clear. In this article, we will build a "real" application while exercising some of the cooler features of LCDS 3 along the way. And check this out: we won't need to write any server-side code!

Why Model Driven Development?

Why? is certainly a valid question, along with What does it do for me, the developer?, and my favorite because I'm a die hard skeptic Is this just marketing buzzwords? To answer all of these questions, we first need to talk about the current state of affairs of enterprise RIA development. Here is a diagram of a standard application:

We have a pretty user interface on one side and a big database on the other side. Going from the bottom of the stack up, we have an object-relational mapper, like Hibernate, that moves data from the database to more friendly domain entities, like POJOs, that we use on the server. Next, there are some server-side services that are responsible for getting the POJOs onto the wire and down to the client (really many clients because everything is multi-user). On the client-side, we also have a set of services for getting data off the wire and into client-friendly entities. For Flex-based frontends, I call the client-side domain objects PASOs (pronounced PAY-so) which is short for Plain ActionScript Objects. Finally, we have a UI that displays and manipulates the PASOs. Any modification operations on the client trigger the reverse process, moving data off the client, up to the server, and eventually to the database.

In model driven development, the model is a description of the domain: entities, their properties, the relationships between entities, etc. In the diagram above, the model directly describes the POJOs on the server and the PASOs on the client. Also, by adding a little bit of metadata, it is possible to infer the database schema, so the model can be used to describe the database. Adding even more metadata, can provide a description of the available services, both server-side and client-side. Without the model, and model driven development, the system quickly becomes a hodgepodge of layers. Everything becomes the responsibility of the developer, who must implement a significant amount of boilerplate plumbing code at every layer in the system. Frameworks help, but they only go so far.

In LCDS 3, you write the UI and the model, and LCDS does everything else. Yep, LCDS handles everything below the UI using just the model. Not only that, but LCDS is constructs a performant, enterprise-class backend using just the model. No boilerplate code, no plumbing code, no serialization/deserialization, no Hibernate config, just a point-and-click Modeler and your UI code. Game changing.

Developing with LCDS 3

Once upon a time, LCDS was exclusively a server-side tool. It lived only on the server, and all LCDS development took place on the server. The act of wiring an LCDS backend to a Flex frontend was entirely up to the developer. LCDS 3 is quite a bit different. Here is a diagram of the new LCDS, highlighting the relevant pieces used during development:

As you can see, the old LCDS is mostly still there. It's the server-side webapp piece that runs inside Tomcat (other servlet containers are also supported including JBoss and WebSphere). New to LCDS 3 are the Modeler plugin, Remote Development Services (RDS), and the Fiber platform. The Modeler plugin adds a new Data Model perspective to Flash Builder 4, but it does quite a bit more, handling all of the client-side code generation (with help from FB4's new Data Services) and all communication with the server via RDS. On the server, the Fiber platform does the real heavy lifting. It manages all the clients, keeps all the data synchronized, handles all the server-side code, and it even manages the database schema. All this behind-the-scenes magic means that the developer can build a model, connect to a database, and push the schema all the way to the database without ever leaving the comfort of the IDE.

A "Real" Application

Dragging and dropping services into a DataGrid is great for demos and screencasts, and really shows of the depth of the integration between the Modeler plugin and the Flash Builder 4's new Data Services, but when I'm building an enterprise application for a demanding client, I need total control. In this article, we will see how LCDS 3 does much more than manage simple forms and DataGrids by using it to bind data to a user interface that's too complex to specify through Flash Builder's drag and drop designer.

Our "real" sample application will be a blog application consisting of authors, posts, and tags. Authors write posts, posts have tags, and users read posts. We'll build an end-to-end application with an LCDS backend and a Flex 4 frontend, and I won't skimp on the details in between. I will limit the frontend to adequate, so no pretty skins or pretty effects. (The eye candy will be delivered in a future article.)

UML

Like all good enterprise developers, we begin our enterprise application development journey at the beginning: with a domain model. Here it is modeled in vanilla UML:

UML is deep and powerful and even useful. Our domain model shows all the entities in our system, their properties, and their relationships in a nice diagram. I've ignored all the methods, because including getters and setters really muddies the picture. It's also important to remember that a domain model describes entities and not database tables.

For those unfamiliar with UML notation, most of the above is self-explanatory, but we should clarify that the leading slash on some properties denotes that they're derived, and the filled diamond on an association line denotes a composite relationship, which among other things means cascading deletes. In our domain model above, numPosts, daysOld, and wordCount are derived properties. Derived properties are not stored in the database, instead they are calculated from the values of other properties in the domain model. For example, wordCount will be calculated from the number of words in Post's content property. Also in our domain model, a one-to-many composite relationship joins authors to posts. Consequently, deleting an author will also delete all of their posts. All this information and more packed into one tiny little diagram. That's what I call useful.

Getting Started

Rather than boring you with the details of installing and configuring LCDS 3 and the underlying relational database, I'll just give you the summary below. I recommend the official docs and a two-part tutorial from my blog (part 1 and part 2), for more detailed instructions. Here is a much abbreviated version:

  1. Install the latest betas of everything (FB4, LCDS 3, Modeler Plugin), and install MySQL.
  2. Create a blank database.
    CREATE DATABASE blog;
    CREATE USER 'blogger'@'localhost' IDENTIFIED BY 'password';
    GRANT ALL PRIVILEGES ON blog.* TO blogger@localhost;
    
  3. Create a new LCDS web application (by copying and pasting the template lcds webapp) and configure it to use the database you just created.
    <Context privileged="true" antiResourceLocking="false" antiJARLocking="false" reloadable="true">
        <!-- JOTM -->
        <Transaction factory="org.objectweb.jotm.UserTransactionFactory" jotm.timeout="60"/>
    
        <!-- MySQL -->
        <Resource name="jdbc/blog" auth="Container" type="javax.sql.DataSource"
            maxActive="100" maxIdle="30" maxWait="10000"
            username="blogger" password="password"
            driverClassName="com.mysql.jdbc.Driver"
            url="jdbc:mysql://localhost:3306/blog?autoReconnect=true"/>
    </Context>

Now that we have the LCDS backend is up and running on a blank MySQL database, we need to fire up Flash Builder (with the Modeler plugin) and get the frontend wired to the backend. Again, I'll given some abbreviated instructions below and recommend Part 2 from my blog for screenshots and all the gory details.

  1. Create a new Flex Project with an LCDS backend and configure it to use to the new LCDS web app you just created.
  2. Create a blank model.
  3. Setup RDS to talk to the new LCDS web app you just created.

Going Modeling

Finally, the real fun begins. When you switch to the Data Model perspective, you'll see the Modeler's design view which is basically a limited UML modeler specifically tailored to the feature set provided by LCDS. The next step is to use our UML domain model above as the basis for constructing our LCDS model. Recall from our discussion above, the model in LCDS is a domain model, plus additional metadata to describe the services and the database.

At last, it's drag-and-drop time! Since we are starting with a blank database, we must create everything ourselves. First, we create our entities, Author, Post, and Tag, and mark them all as persistent because we want LCDS to store them in the database. Then, we give each entity a numeric id property and make sure it's set as auto-generated. Lastly, create the all the simple properties on each entity. Our model should look like this:

Next, we'll add the three derived properties: numPosts, daysOld, and wordCount. Again, just drag-and-drop a simple property onto an entity and give it a name. To enter the derived property's value expression we can just type it in using the Properties panel, shown here:


Alternately, we can use the very cool Expression Builder by just clicking the button. In the case of an author's numPosts, we use the handy built-in function sizeof() to count the number of posts an author has written (we haven't added the posts relationship yet, but we'll get to that in a moment). Here's a screenshot of the Expression Builder, showing the function browser:

Similarly for the derived property daysOld, we use the dateDiff() built-in to compute the age of a given post in days: dateDiff('d', pubDate, now()). For wordCount, we'll have to write our own custom function, therefore we just enter, getWordCount(content) as the value expression.

With all of its properties, simple and derived, our model looks like this:

To create a custom getWordCount() method we have two implementation options: server-side in Java or client-side in ActionScript. Since this is a no-Java article, the decision is obvious, but in a real-world app, we might choose to implement it server-side in Java to address security, performance, or integration issues. To inform the model of our implementation choice, we click Post and create a new method using the Properties panel:

This instructs the code generator to stub out the getWordCount() method (with this signature: getWordCount(text:String):int). We will override it later in this article to calculate the number of words in a post.

Model Relationships

The next aspect of modeling we'll tackle is relationships. Conceptually, relationships wire our entities together. In the Modeler, relationships are aptly represented by lines. Taking a step back, but not getting too deep into the theory, relationships can be one-to-one, one-to-many, or many-to-many. The type of relationship is determined by the cardinality of each side. Cardinality specifies how many instances of an entity relate to one instance of another entity, and is represented on the model by little annotations on each side of the relationship line. Lastly, relationships can also be directional, represented in the Modeler by an arrow, or bi-directional, represented by the absence of any arrows.

Let's dive into some examples to illuminate the situation. First, add the author property to Post and the posts property to Author. Then set author as type Author and posts as type Post[]. Finally, set Author.posts as mapped by Post.author. When you select the relationships line, you see the author-to-posts relationship in the Properties panel:


The panel shows us we have a one-to-many relationship between author and posts. Post has an author property which is associated with one author (cardinality = 1), and Author has a posts property which is associated with zero or more posts (cardinality = 0..*). The Properties panel even spells this out for you: "A property in multiple instances of Post can refer to the same instance of Author."

Here is the resulting XML for the author-to-posts relationship:

<entity name="Author">
    ...
    <property name="posts" type="Post[]" mappedBy="author" />
</entity>
<entity name="Post">
    ...
    <property name="author" type="Author" />
</entity>

In a relational database, relationships between entities are represented by foreign keys (a foreign key is just a key in one table that points to the primary key in another table). When deployed to the server, our model will be used to create the database (see the Model Annotations section below for more details). So for the one-to-many author-to-posts relationship, the Post table will have an author_id foreign key column that points to the Author table.

Next, add the many-to-many relationship between posts and tags to our model by following the same steps as above, except this time both properties are collections. Since this relationship is totally symmetric, we arbitrarily choose to make Post the owner, so set Tag.posts as mapped by Post.tags. When the database is created, the many-to-many posts-to-tags relationship will be stored via a Join table with a pair of foreign keys, one for Post and one for Tag.

Here is the resulting XML for the posts-to-tags relationship:

<entity name="Tag">
    ...
    <property name="posts" type="Post[]" mappedBy="tags" />
</entity>
<entity name="Post">
    ...
    <property name="tags" type="Tag[]" />
</entity>

And here is our finished model, including both of our pretty relationship lines:

Model Annotations

As it stands right now, our model does not contain enough information to enable the RDS servlet to do its job and deploy our model to the server. The database is not configured, the client-side code generation is not configured, and entities are not mapped to tables. All of this information must be added to the model via annotations. There are three important groups of annotations:

  • DMS (Data Management Service) - covers server-side services and the database.
  • ServerProperties - covers server-side configuration.
  • ActionScriptGeneration - covers client-side code generation.

First, we'll use some DMS annotations to map our model entities to database tables and properties to database columns. This step is required since we are starting with a blank database. We'll use the following naming conventions in our database: everything lowercase, pluralized table names, and underscore as the word separator in column names. The Modeler provides a nice UI on top of all annotations, enabling you to add everything through the Properties panel, but for the repetitive process of adding the same annotation to multiple entities I often find it faster to work directly with the underlying XML. So in the next section I'll show little snippets of the XML source instead of pretty screenshots of the Modeler.

So, the Author entity maps to the authors table:

<entity name="Author">
    ...
    <annotation name="DMS">
        <item name="Table">authors</item>
    </annotation>
</entity>

Similarly, Post maps to posts and Tag maps to tags. The only property that needs to be mapped is the pubDate property, which becomes the pub_date column:

<entity name="Post">
    ...
    <property name="pubDate" type="date">
        <annotation name="DMS">
            <item name="ColumnName">pub_date</item>
        </annotation>
    </property>
</entity>

Next, we apply a ServerProperties annotation to all entities to inform RDS that they are backed by LCDS. Again, working directly with the XML source:

<entity name="Author">
    ...
    <annotation name="ServerProperties">
        <item name="ServerType">LCDS</item>
    </annotation>
</entity>

Then, we apply an ActionScriptGeneration annotation to all entities because we want our client-side code to use the dms package:

<entity name="Author">
    ...
    <annotation name="ActionScriptGeneration">
        <item name="Package">dms</item>
    </annotation>
</entity>

Next, we configure the database connection (based on our Tomcat webapp configuration above) by adding a pair of DMS annotations on the model itself:

<model xmlns="http://ns.adobe.com/Fiber/1.0">
    <annotation name="DMS">
        <item name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</item>
        <item name="datasource">java:/comp/env/jdbc/blog</item>
    </annotation>
    ...
</model>

Finally, since LCDS 3 defaults to lazy loading all relationships, we'll add a special DMS annotation to both sides of the author-to-posts relationship to turn on eager loading:

<entity name="Author">
    ...
    <property name="posts" type="Post[]" mappedBy="author">
         <annotation name="DMS">
             <item name="lazy">false</item>
         </annotation>
    </property>
</entity>
<entity name="Post">
    ...
    <property name="author" type="Author">
        <annotation name="DMS">
            <item name="lazy">false</item>
        </annotation>
    </property>
</entity>

We do this because of our foreknowledge of how our blog frontend will work. Since we never expect to process an author without his or her posts, or a post without its author, we choose to turn on eager loading so that posts are retrieved along with their associated author, and vice versa. Now that we've added all of the necessary annotations, our model contains enough information for RDS to successfully deploy it to the server and create the database.

Filters

When a model is deployed (or updated), the Fiber platform does the work of configuring the server-side services via metadata from the model. Besides saving us from writing any Java, this process creates, among other things, a set of fill functions for each entity. Fill functions are simply server-side wrappers around database queries, that are in turn wrapped by client-side services to provide a complete set of query methods for each entity. And best of all, everything is under the control of Data Managment Services in LCDS. So when an entity is changed, the changes to the entity, and any fills involving the entity, are automagically pushed to all clients.

By default, a client-side entity service will include the getAll() fill function, plus one for every property and relationship on the entity. For example, Author has id, name, and posts, so the AuthorService provides the following fill functions:

  1. getAll():AsyncToken
  2. getById(id:int):AsyncToken
  3. getByName(name:String):AsyncToken
  4. getByPosts(posts:Post):AsyncToken

But what do we do if we want to query authors by post title or query posts by a date range? Again, LCDS provides a simple mechanism to add custom fill functions called filters. Filters come in two flavors, criteria-based filters and pass-through filters. They can be added to entities in the data model via the Properties panel or the underlying XML.

Here we add a getAllSorted() filter with only an order by clause (leaving criteria blank):


In XML they are added to the Post entity:

<entity name="Post">
    ...
    <filter name="getAllSorted" criteria="" order="pubDate DESC"/>
    <filter name="getByAuthorNameFuzzy" criteria="author.name like" order="pubDate DESC"/>
    <filter name="getByTagNameExact"
        arguments="tagName:string"
        query="jpql:Select p From Post p Join p.tags t Where t.name = :tagName Order By p.pubDate DESC"/>
</entity>

As shown in the Properties panel, the getAllSorted() filter is a criteria-based filter without a criteria. Since it has only an order by clause, I like to call this type of filter a sort filter. This filter is simply a server-side sort (which I assume is actually handled by the database). It delivers a nicely sorted collection of posts to our Flex frontend. The second filter, getByAuthorNameFuzzy(), is also a criteria-based filter, this time we retrieve all posts that match the given author's name. The matching is fuzzy due to the use of the like string comparison function. The last filter, getByTagNameExact(), is a pass-through filter. Here we use a fully formed JPQL query that is "passed through" to the database to retrieve all posts that match the given tag name exactly. This is done via a Join between posts and tags (see the JPQL docs for details).

And that's it. We are done with our model. All we need to do now is just click the tiny "Deploy Model to LCDS Server" icon and away we go: the database tables will be created and all the server-side configuration will occur.

Generated Tables

Once the model is deployed to the server, the database schema is generated by LCDS. Since we decided to start with a blank database, we were required add metadata to our model to map entities to tables (see Model Annotations above). Therefore, a dump of the database table structure holds no surprises:

CREATE TABLE authors (
  id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(255),
  PRIMARY KEY (id)
);

CREATE TABLE posts (
  id int(11) NOT NULL AUTO_INCREMENT,
  title varchar(255),
  content varchar(255),
  pub_date datetime,
  author_id int(11),
  PRIMARY KEY (id),
  KEY FK_author_TO_Author (author_id)
);

CREATE TABLE tags (
  id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(255),
  PRIMARY KEY (id)
);

CREATE TABLE posts_tags (
  posts_id int(11) NOT NULL,
  tags_id int(11) NOT NULL,
  KEY FK_posts_TO_Post (posts_id),
  KEY FK_tags_TO_Tag (tags_id)
);

As expected, we have three entity tables, plus a Join table for the many-to-many relationship between posts and tags. Each table has columns for entity properties and id as the primary key. There is a foreign key on the posts table referencing an Author, and a pair of foreign keys on the Join table.

But there is one issue: by default a model property of type string is mapped to varchar(255) in the database. Usually, this is fine, but for the content property of Post, we need something bigger. No problem, just give content a length of 25,000 in the Properties panel:


Re-deploy our model and we get a varchar(25000). This mapping is obviously database dependent, but since I'm using MySQL 5.1, varchar can hold up to 65,000 characters or so (see the MySQL docs for details). If I use a length bigger than that, the mapping switches to mediumtext in MySQL. On the client, the Post PASO uses a standard ActionScript String for the content property, so there's nothing to worry about. And hidden from us on the server, the Post POJO uses a Java String.

Generated Code

When the model is updated, the client-side code generation occurs. Five classes are generated for each model entity: two user-modifiable classes (typically referred to as extension classes) including a value object class and a service class, and three internal classes including a value object parent class, a service parent class, and a metadata class. The user-modifiable extension classes are blank, and acquire all of their functionality by extending from the internal class. The internal classes are regenerated each time the model is updated, and should not be modified. For example, the Author entity initially generates: Author.as, AuthorService.as, _Super_Author.as, _Super_AuthorService.as, _AuthorEntityMetadata.as.

The value object extension class contains (via inheritance) all the public getters and setters for the properties and public getters for the derived properties. It also provides validation, simple caching, and access to the object's metadata via the _model property. I tend to think of these classes as PASOs despite the fact they are more then pure domain objects. Similarly, the generated service classes make up the client-side service layer, containing the basic CRUD operations for each entity, plus the default and custom fill functions.

From our model, and the UML data model that it is based on, we know that there are two pieces of custom functionality we need to add to the client-side service layer. First, the Post entity has a derived property wordCount that needs a custom implementation of the getWordCount() method. Second, the author-to-posts composite relationship requires a custom implementation of cascading deletes.

We add our implementation of getWordCount() to Post.as:

package dms {
     [RemoteClass(alias="Blog.Post")]
     public class Post extends _Super_Post {
          override protected function getWordCount(text:String):int {
               return text.split(/\s+/).length;
           }
          ...
      }
}

The parent class _Super_Post stubs out the function for us, but throws an error, so we are required to override the method in the extension class. Eschewing the need for anything fancy, we simply count spaces as a rough estimate of word count.

Since LCDS 3 doesn't provide support for cascading deletes, we are forced to write our own custom implementation:

package dms {
     import mx.rpc.AsyncToken;
 
     public class AuthorService extends _Super_AuthorService {
          private var postService:PostService = new PostService();
  
          override public function deleteAuthor(author:Author):AsyncToken {
               //first, delete all associated posts
               author.posts.toArray().forEach(
                   function(post:Post, idx:int, posts:Array):void {
                        postService.deletePost(post);
                    });
   
               //second, delete the author
               return super.deleteAuthor(author);
           }
      }
}

Reading the function above, we first walk all the author's posts and delete them, before deleting the author itself. Fairly straightforward, but there are a few non-obvious bits. First, recall that we turned on eager loading for both sides of the author-to-posts relationship, so the author's posts collection is already hydrated. So when we write author.posts.toArray() the posts are immediately available, and we do not initiate an asynchronous call to the server. Second, based on my somewhat limited understanding of LCDS internals, there is no danger of a crazy race condition (author being deleted before their posts), because all non-read operations are done in the order they are received.

I also like to add my own constructor to the generated PASOs to include some sensible defaults for all the properties. In particular, I dislike the LCDS default of setting all collection properties to null, so I will typically initialize them to an empty ArrayCollection.

Testing

At this point, the backend is done. And by done, I actually mean production-ready done, just point the Modeler at RDS running on the production server and push. But since we are good enterprise developers and always maintain a healthy level of skepticism, we must to prove to ourselves that the backend is done. The only way to do this is to test it. As we are already living in beta land, we will continue on the beta path and use FlexUnit 4 Beta as our unit testing framework. FlexUnit 4 is guaranteed to make all enterprise developers happy: it is fully metadata driven, it has awesome async support, and it plays nice with continuous integration. Without dwelling too much on the details of the testing framework (you can read more in the feature overview and this tutorial), let's write some code.

Here is a portion of a FlexUnit 4 test that verifies our custom cascade delete actually works:

[Test(async)]
public function deleteAuthor():void {
     //get author by name & wait for response
     var token:AsyncToken = authorService.getByName('Vernor Vinge');
     token.addResponder(Async.asyncResponder(this,
         new TestResponder(waitForGetAuthor, faultHandler), 5000));
}

private function waitForGetAuthor(data:Object, passThroughData:Object):void {
     var author:Author = (data.result as IList).getItemAt(0) as Author;
     Assert.assertEquals('Bad author name', 'Vernor Vinge', author.name);
 
     //delete author (which should cascade delete his posts) & wait for response
     var token:AsyncToken = authorService.deleteAuthor(author);
     token.addResponder(Async.asyncResponder(this,
         new TestResponder(waitForDeleteAuthor, faultHandler), 5000));
}

private function waitForDeleteAuthor(data:Object, passThroughData:Object):void {
     //get author by name
     var token:AsyncToken = authorService.getByName('Vernor Vinge');
     token.addResponder(Async.asyncResponder(this,
         new TestResponder(verifyDeleteAuthor, faultHandler), 5000));
 
     //get post by title
     var token2:AsyncToken = postService.getByTitle('A Fire Upon The Deep');
     token2.addResponder(Async.asyncResponder(this,
         new TestResponder(verifyDeletePosts, faultHandler), 5000));
}

private function verifyDeleteAuthor(data:Object, passThroughData:Object):void {
     //verify getByName returned nothing
     var authors:IList = data.result as IList;
     Assert.assertEquals('Author not deleted', 0, authors.length);
}

private function verifyDeletePosts(data:Object, passThroughData:Object):void {
     //verify getByTitle returned nothing
     var posts:IList = data.result as IList;
     Assert.assertEquals('Post not deleted', 0, posts.length);
}

Due to the asynchronous nature of the backend, the test code is a little long-winded. Basically, we query for a specific author by name, delete him, and then verify that both the author and all of his posts are gone. Everything else is the necessary bloat that comes with asynchronous testing. It's a little hard to believe from looking at the code, but FlexUnit 4's async testing support is first rate.

What Have We Done?

The conclusion of Part 1 is upon us, so let's review our work so far.

We have:

  1. Built a data model with properties, derived properties, and relationships.
  2. Created derived properties using both built-in functions and a custom method.
  3. Created both one-to-many and many-to-many relationships.
  4. Created custom server-side filters to return collections of entities to the client.
  5. Annotated a data model for deployment to a blank database.
  6. Implemented cascading deletes and custom derived property in ActionScript. No Java.
  7. Wrote a unit test against an asynchronous backend.
  8. Deployed our model to the server to create a production-ready backend.

Not too bad, especially point #8, for a very minimal amount of effort. Model driven development with LCDS is unbelievably fast. And for other half of the question: what did we skip? We left out a number of couple of major LCDS 3 features including pagination, constraints, variants, styles, and all server-side code (the Java part).

Stay tuned for Part 2, where we will close the loop on model driven development and build a complete Flex 4 frontend on top of our LCDS backend.

Read more from Justin Shacklette. Justin Shacklette's Atom feed

Comments

8 Comments

Tom said:

Great article. I look forward to part 2 next month.

Patrick said:

Amazing and thoughtful job. You really know your stuff.

dsi r4 said:

I am newbie to LCDS. Is the same procedure can be used well with Flex Builder 3.3, as I am not getting the databases-view in the Flex Builder.

Justin said:

Of course, LCDS 3 has been released and can be downloaded from adobe.com.

@dsi r4: You need both the Modeler plugin and the latest beta of Flash Builder 4 to use the model driven development features of LCDS 3.

Todd said:

Brilliant....... I've been seeking such an explaination for over 1 month and you've provided all of the answers in 11 printed pages.... BRAVO!!!!

Thank You

Aseem Behl said:

Hi Justin,
Nice Article! I followed all the instructions in this tutorial, but I am stuck at the following issue:
getAllSorted, getByTagNameExact and getByAuthorNameFuzzy functions are not being auto generated in the _Super_PostService.as. Am I required to manually add these functions to the class or is there a step missing in the tutorial which takes care of that.

Thank You

Justin said:

@Assem: What I think you are seeing is a very common issue with the Modeler plugin (which is still beta). Unfortunately, it sometimes seems like it doesn't automatically regenerate the code when you make changes to the model.

Here's a helpful trick: Switch to the Flash Builder perspective, go to the Data/Services view, and click the Edit Active Data Model icon. For some unknown reason this seems to force code regeneration.

Helpful trick #2: Don't forget to re-deploy your model after you make changes. This one gets me frequently.

Rudd said:

Good post, I followed all the instructions in this software tutorial, but I am stuck at the following issue: getAllSorted and getByAuthorNameFuzzy functions were not being generated automatically in the _Super_PostService .

Thank You

Leave a comment


Tag Cloud

iPad

What's your take on the iPad? (Putting aside the Flash/iPad flame war)

Answer

Latest Features

Recommended for You

@InsideRIA on Twitter

Archives

  • Or, visit our complete archive.  

About This Site

Welcome to the premiere community site for all things RIA sponsored by O'Reilly Media and Adobe Systems Incorporated.