Handouts: Music Project UI and Page Flow, Music Project Supplied
Sound Files (2 sides of one page)
Last time, UI (presentation layer code) with state held across multiple calls to the service API : room number as example.
Idea of app: selling CDs for a band. CD=Product
music1 = client-server implementation, like pizza1, so boring line-oriented UI. But aimed at web site, with proper web pages, as shown in Murach. Let’s look at them. See handout on Music Project UI and Page Flow. Will look at it further in a moment.
PA1: you need to design the service API. The presentation code is sketched out, but not how it calls down. This is a crucial part of database app design. Each call should be “self-contained” in the sense we discussed last time. You can use the service API for pizza as a model.
Note that Murach talks about the layers (see pg. 17), but in fact does not supply a service API, so you need to design one yourself.
Lack of service API in Murach: Look at Murach, pg. 654 in CatalogController, presentation code, where the email of a user is found in a cookie, then calls directly into UserDB, a DAO (implemented by a static class rather than a singleton). We want to have presentation code call a service API, and let the service method call the DAO.
user = UserDB.selectUser(emailAddress); //call from presentation code directly into UserDB, a static class
Note that the database is allowed to hold user-private data, but the need for a user id means this is only useful after the user has “logged in” explicitly or implicitly (i.e. using cookies in a web app.)
Domain and Transfer Objects
Recall PizzaOrder vs. PizzaOrderData
User for core code, UserData for transfer to presentation and storage in presentation. It turns out these have identical fields, so if you want to drop UserData (changing uses of it to User), that's OK. The idea was that User.java has many more fields than UserData. User.java is really meant to have all the fields shown on pg. 663, but we've simplified it down to the basics. Clearly fields like creditCardNumber should be kept as secret as possible.
Invoice for inside service API, InvoiceData for presentation: this is much simpler than Invoice with referenced User and LineItems.
Download vs. DownloadData: also simpler.
Product, Track: invariant, "reference data", use for both inside and outside. This app does not support adding a Product or a Track. Of course in reality we could bring the app down, add a Product, and bring it up again. But during operation, the set of Products and the Product details remain fixed.
Cart, LineItem: private to user while gathering CDs for eventual purchase (or not) More on this soon.
Look at package domain and package service.data
UI in supplied UserApp.java.
Note that in general, it is difficult to convert a web UI into a line-oriented UI because a web UI has an arbitrary graph connecting different executable units, whereas a normal program has a tree of calls of different executable units. On the other hand, a tree-like web site is user-friendly, so many websites are basically treelike.
Music is organized in a tree-like way: Look at handout on Music UI, Page flow for music
UserApp: supplied code, you just add calls to your service layer. Note that it combines the Product and Sound pages into one method processProduct. This makes sense when you note that both these pages need to allow the user to add to cart and display cart and get back to Catalog.
You should not have to change the control structure of the supplied UserApp.java.
What about the Cart for music? It’s domain data not held in the database...
First note that a Cart is specific to a user, i.e., user-private data, not shared data among users. Following Murach, we’re not saving carts to the database. They just go away when the user buys some CDs or abandons the process. The resulting Invoice object from the purchase is of course put in the DB, along with its LineItems.
But carts are domain data. They could be saved to the DB, and many actual sites do this. In our case, they need to be saved over time and service calls in the presentation layer since they can’t be saved in the service layer by the statelessness rule.
That is, there is a field holding a Cart object in the presentation layer class but no such field in the service layer class. See UserApp.java for field cart (commented out for now.)
The service layer can still act on Carts: we just have a method with a Cart argument. We want to put all the important code for the app in the service layer, not hidden in the presentation layer, because different UIs could talk to the user, but they all should work with Carts the same way.
Note we want to do the needed “new Cart()” in the service layer, since that action is needed whatever the UI is, by the Thin Presentation Layer rule. Yet we need to hold the resulting Cart object in a field in the presentation code. No problem: Cart createCart() in the service API, result saved in presentation. In UserApp, we would have cart = xxService.createCart();
Presentation variables in Music holding domain data across calls to the service layer
Currently commented out in UserApp.java: fields holding domain data, all specific to the current user. You will need to uncomment these when filling out UserApp.java.
private UserData user; // once registered, non-null
private Cart cart; // the CDs selected so far by the user (registered or not), but not yet bought, and not in DB
We see that the Cart object belongs to the presentation layer, but the action of creating a Cart can be done by the service layer, in a “Cart createCart(…)” call. We want the service layer to do all the important actions of an app.
Also, inside processProduct, local variable product holds the current CD being examined by the user, across calls to the service layer.
So the Product object saved in presentation layer does live a while. It represents a specific user’s choice, i.e., user-private data.
Now look at relationships among the domain objects for Music, will find unidirectional relationships (in terms of inter-object refs)
We found state held across multiple calls to the service API : Cart, User, Product objects. Cart cart and UserData user are fields of UserApp, Product is a local variable of processProduct that holds state across multiple calls to the service API, so is “longer term” state in the system. Note that these are all user-private variables, as are all variables defined in the presentation code.
Previously presented ideas about domain objects:
Control structure of UserApp.java: ignore this if you want and just read its code.
For simple line-oriented UIs, a tree structure between activities in a UI makes it easy to implement by method calls and returns. Is there a tree here?
Starting from the page flow diagram, put the Cart box further down on page to see core the graph, not a tree but at least acyclic, plus back arcs to Catalog.
So it is possible to use method calls to move down this graph, back up on return.
Static Call graph for the supplied UserApp.java: an arrow from a box means there is a call in the box’s method to the target method
The returns from the methods can climb back up to Catalog. The code makes sure that a return from Cart to Product is followed immediately by a return back to Catalog, so the user never makes the transition from Cart to Product/Sound directly. After seeing the whole cart, the user must go back to the Catalog page to select a certain product. That is required by the full page flow diagram we looked at earlier. Look at it to see there is no arrow from Cart to Product. (Another design could allow such a transition, but ours doesn’t.)
Note that the dynamic calls make a tree: Catalog calls Product/Sound calls Cart, back to Product/Sound, immediately back to Catalog
Catalog calls Cart calls Register, back to Cart, calls Invoice, back to Cart, immediately back to Catalog, and so on.
Aside: There is another way to code UserApp using the “finite state machine” approach. Code one loop at top level with enough state to remember where the user is in the graph and what they can do next. Each “page execution” is done by a call from that loop. The above approach (i.e., what the provided UserApp does) uses the execution stack to remember how to get back from wherever the execution is at the moment. However, this approach needs a nice-enough graph, and those doubled-up returns to skip over Product/Sound on the way back, a “code smell”.
End of coverage of Control Structure of UserApp.java