Resource: Music Project UI and
Last time, defining domain data, designing the service API to allow a stateless service layer by making each service method "self-contained".
"self-contained" method: all the domain data needed for the method's code is provided in the method's arguments. Nothing is built incrementally.
Idea of app: selling CDs for a band. CD=Product
Downloads: records of user downloads of music samples, mp3 files played by web app with the help of the browser in music3
In music1-setup/sound directory. We looked at the directory structure and used a browser to play some sample mp3's. Note that our simple Java programs for music1 will not be able to play the mp3 files, unless you download a special library (not expected).
From Murach’s distribution: one directory for each CD, using the filesystem for decent organization of data. Note that this is all read-only data, so doesn’t need to be in the database as much as changeable data does. The sound.html files have been converted from Murach’s corresponding sound.jsp files (easily done).
There is one sound.html file for each CD, and one is printed out below. Note that the sound.html files list more tracks than we have in the database, but we have the ones with mp3s. See Murach, pg. 657 to see his directory structure: we’re taking his sound directory...
http://www.cs.umb.edu/cs636/music1-setup/sound (also available in the filesystem at /data/htdocs/cs636/music1-setup/sound)
One mp3 for pf02 (description: "Paddlefoot--the second CD"): full URL is http://www.cs.umb.edu/cs636/music1-setup/sound/pf02/neon.mp3
See one directory for each CD for sale, using the product code as the directory name. Inside, find sound.html and mp3 files--play some!
In music1, not easy to actually play these from Java (need special library), so just print out the filename as "playing".
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. HW3 has some practice on API design.
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.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. Although User is immutable in pa1, it has the potential to be mutable as a user address is added to a record of an already-registered user, so we will treat it as mutable.
Invoice for inside service API (where this object is mutable: we mark an invoice as "processed"), 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. So it's OK to let these objects flow into the presentation layer--note there is no Product-related transfer object defined.
Cart, CartItem: private to user while gathering CDs for eventual purchase (or not) More on this soon.
Look at package domain and package service.data
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
Music Project UI and Page Flow.
Look at a page in the Page Flow: Catalog page for example
The Catalog page, also shown on pg. 645, displays the catalog, i.e., the list of CDs. The user can choose a particular CD (for example product code 8601) and get more info on it, or display the current cart (Cart page, see below) or go back to the Home page.
means the user is given choices of what to do next: get more info on
one CD (chosen), see the current Cart, or go back to the beginning
state. We can do this in simple line-oriented UI, as done in the
provided skeleton UserApp.java.
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 the page flow for music and see the tree.
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 (that would be state in the service layer, against the Stateless Service Layer principle) 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();
There is a transfer object CartItemData to simplify the cart contents for the presentation layer. You don't have to use it, though, since the CartItem
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.
Two kinds of domain objects
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
DAO :problem of inter-related objects
Ex: from pizza:
2 finders for PizzaOrders
Set<PizzaOrder> find OrdersByRoom(…) – full details: Toppings, PizzaSize
Set<PizzaOrder> find OrdersByDays(…) – no details! P.O. object has null size ref, null Set<Toppings>
(only used by code that doesn’t need details)
Last time looked at object graphs for Music, saw that starting from Download, we need a User and Track object to fill in details.
Starting from Invoice, quite a lot of objects are referenced directly or indirectly: User, LineItems, then from LineItem, Product, and from Product, Tracks
If start from User, nothing else is ref’d. And so on.
We can use null ref’s to indicate missing detail objects.
Note that if we had bidir relationships everywhere, we would have even more objects ref’d from each object, and it would be hard to know where to draw the line and null-out further refs.
So that’s an additional “cost” to additional relationships in the domain classes: needing to decide where to cut them off.