Data Modeling and Domain Objects

Introduction

In this lab you will have a chance to become better acquainted with the Couchify data models. These domain objects will allow you to more easily perform business logic to validate, mutate and make decisions based on the values of the these domain objects.

What you will gain experience with:

  • Analyzing documents and their JSON constructs

  • Mapping scenarios to different JSON document models

  • Defining Domain Objects from their JSON document equivalents

Estimated time to complete: 45 minutes

Instructions

Reviewing Data Models

Use Case

As you learned from the associated course, there are many different ways the data could be represented for the Couchify application based on a variety of different requirements. In this section, you will take time to analyze some of the key differences in the way the data is modeled in the playlist, userprofile and track documents.

Using the Classic Document Editor

In this section, you will be browsing documents representing the different ways of modeling Playlists, User details, Tracks and so on. You can easily do this by utilizing the Classic Editor in the Buckets view. To access this editor, perform the following tasks.

  1. Open the Couchbase dashboard (http://localhost:8091/ui/index.htm) and select the Buckets menu item on the left side of the page.

  2. Select the Documents link for the couchmusic1 bucket.

  3. Select the Classic Editor link in the upper right area of the page.

    Browsing documents

  4. One feature of the Classic Editor is the ability to easily filter documents by key prefix. To utilize this feature locate the filter button - the down arrow next to the bucket name. Click on this to access the filter

    Browsing documents

    You can enter some text in the startkey field to filter keys with a prefix of userprofile, playlist, track and so on.

Analyzing couchmusic1

  1. Ensure that you are currently looking at documents in the couchmusic1 bucket.

  2. Examine any of the documents and notice that the document type is root node or attribute of the document.

  3. Use the filter to select documents with the userprofile key prefix. Open one of the documents and note that the phones attribute is represented as a single object, which could potentially be a set of key/value pairs for different phone types.

  4. Use the filter to select documents with the track key prefix. Open one of the documents and note that each track has an array of ratings, each of which can reference the userprofile document using the value for the username attribute.

  5. Use the filter to select documents with the playlist key prefix. Open one of the documents and note that the tracks are merely an array of track id values. These can be used to build references to the documents of type track.

  6. Use the filter to select documents with the country key prefix. Open one of the documents and note that the sub-region attribute references a simple embedded value as a String type.

Analyzing couchmusic2

  1. Use the Bucket selector (see above image) to switch to the couchmusic2 bucket.

  2. Examine any of the documents and notice that, the type of document is no longer a root element, but is now just another attribute of the document.

  3. Use the filter to select documents with the userprofile key prefix. Open one of the documents and notice now that the phones attribute references an Array and each element of the Array is an object with multiple attributes.

  4. Use the filter to select documents with the track key prefix. Open one of the documents and note that in this version, an avg_rating attribute has been added.

  5. Use the filter to select documents with the playlist key prefix. Open one of the documents and note that this version includes an attribute called owner, which is an object containing partially duplicated data from the userprofile type document. This can make it easier to access key information from a playlist without having to fetch the associated User Profile document.

  6. Use the filter to select documents with the country key prefix. Open one of the documents and note that now, the region_number attribute references a number that can be used to access the new document type; sub-region

  7. Use the filter to select documents with the sub-region key prefix. Notice the data for a sub-region contains additional information, including its parent region.

Analyzing couchmusic3

  1. Use the Bucket selector (see above image) to switch to the couchmusic3 bucket.

  2. Use the filter to select documents with the userprofile key prefix. Open one of the documents and notice now that there is now an attribute called playlists, which is an Array of summary playlist information for all the playlists owned by this user. Why is this useful? Imagine upon login, a user may want to immediately see their playlists. With this minimal information embedded in the User Profile document, it is not necessary to perform a subsequent query to get the playlists.

  3. Use the filter to select documents with the track key prefix. Open one of the documents and note that rather than the ratings attribute referencing an array of rating objects, it now contains a set of objects, each of which is keyed by the username. This will be examined further in the next section.

  4. Use the filter to select documents with the playlist key prefix. Open one of the documents and notice now instead of the tracks attribute being a list of track ID, part of the track data is instead embedded for each track. With partial track data embedded in the playlist, it is no longer necessary to access the individual track documents in order to see details, such as the Artist, Song Title, link to the MP3 file, etc.

  5. Use the filter to select documents with the country key prefix. Open one of the documents and notice an additional attribute for updated, which is a timestamp of when the country data was last updated

  6. Use the filter to select documents with the sub-region key prefix. Open one of the documents and notice that an Array of country ID values has been added as a back reference. Why is this helpful? This allows navigation from both the country to the sub-region and from the sub-region back to the country.

Choosing a Domain Model

Using data model to enforce application business rules

Take a moment to think about the way in which the Couchify application will manage the rating of tracks. If a particular user were to rate a track more than once, what should the business rules be associated to that? Assuming you want to allow a user only one rating per track, how might this be enforced?

Use the filter to select documents with the track key prefix for the couchmusic1 bucket. Open one of the documents and notice with this structure, ratings are managed as an Array of ratings objects. Therefore, it is possible for a user to potentially create more than one rating unless strictly enforced by application business logic. Consider this fragment of the track document showing some of the ratings as an example.

    "ratings": [{
      "created": "2015-08-18T12:35:25",
      "rating": 3,
      "username": "optioninginlays85689"

    }, {
      "created": "2015-08-18T16:06:18",
      "rating": 4,
      "username": "boisterousnessinurement8"
    }, {
      "created": "2015-08-18T16:06:18",
      "rating": 4,
      "username": "boisterousnessinurement8"
    }, {
      "created": "2015-08-18T16:06:18",
      "rating": 4,
      "username": "boisterousnessinurement8"
    }],

This need not be taken care at the application end, this can be done implicitly based on the design of the data model. Switch to the couchmusic3 bucket and open one of the track documents. The document fragment below shows that rather than the ratings attribute referencing an array of rating objects, it now contains a set of objects, each of which is keyed by the username. The advantage of this data model is that if the user boisterousnessinurement8 rates the track multiple times, the ratings item will be over-written and multiple entries will not be accommodated.

    "ratings": {
      "boisterousnessinurement8": {
        "created": 1439931978000,
        "rating": 4
      },
      "optioninginlays85689": {
        "created": 1439919325000,
        "rating": 3
      }
    }

Relating Playlists and Owners

Imagine a scenario where you want the Couchify application to enable users to browse public playlists for tracks that they may wish to add to their own playlists. It may be of interest to know something of the owners first name, last name and picture of the owner. Using the data model for playlist documents from the couchmusic1 bucket, this would require a subsequent get() operations to fetch the userprofile document.

In the couchmusic2 bucket, note that part of the userprofile document are duplicated in the playlist document. Notice that whereas the owner attribute in couchmusic1 references a String value representing the username, in couchmusic2, this attributed references a JSON object.

{
  "created": "2014-12-16T17:30:45",
  "id": "0001b5ce-b28c-4e00-8a22-541f6728fa62",
  "name": "Playlist # 1 for Belen",
  "owner": {
    "created": 1432120440000,
    "firstName": "Belen",
    "lastName": "Ibañez",
    "picture": {
      "large": "https://randomuser.me/api/portraits/women/55.jpg",
      "medium": "https://randomuser.me/api/portraits/med/women/55.jpg",
      "thumbnail": https://randomuser.me/api/portraits/thumb/women/55.jpg
    },
    "title": "Miss",
    "updated": "2015-08-25T10:27:38",
    "username": "immunologistdispossession6223"
  },
["41D7B885A02A4550F883EEF58F7E29FF9FBE5B8F", "98B1A620AD95E0128639B9446FCDFC098C97E9F9", "136817E6BF9E9B8205896791B7306C4825600998", "DDDAB594A62D490AAC0BFAA4E7CAF142A0701406", "5F7A2165A85CC6A30A65A5E9CA73DE6DFA805357", "381F670B29F17E68E9D06C24E1F12A46BEB9FE42", "E560B4C81F9981ADE9B41AB69447192B33295D9A", "E7E018223C9C5350C61E131A902890CD0DA9C95E", "266FB2D4381E286DBF9918169B9C268FAABF9188"],
  "type": "playlist",
  "updated": "2015-09-11T10:39:48",
  "visibility": "PRIVATE"
}

What is the downside of duplicating part of this information in the playlist document? It is now necessary to ensure that the whenever the userprofile document is updated, all associated playlist documents must eventually be updated as well. Depending on the application requirements, the timing of this synchronization need not occur immediately.

Modeling for user login behavior

Consider a situation where in you need to login to the Couchify application which needs to show the user details as soon as you login along with the basic playlist information for playlists you own.

Using the couchmusic2 version of the userprofile document would require subsequent queries to get all associated playlists. However, the way userprofile is modeled in the couchmusic3 bucket allows for basic playlist information to be fetched along with the user details. The full details for an individual playlist can be fetched as needed.

{
   "userprofile":{
      "address":{
         "city":"jersey city",
         "countryCode":"US",
         "postalCode":47903,
         "state":"south carolina",
         "street":"5847 n stelling rd"
      },

      "created":1417745488000,
      "dateOfBirth":371106000000,
      "email":"nellie.henry@love.com",

      "favoriteGenres":[
         "Post-Bop",
         "Cabaret",
         "Power Metal"
      ],

      "firstName":"Nellie",
      "lastName":"Henry",

      "phones":[
         {
            "number":"(723)-066-7699",
            "type":"cell",
            "verified":1441998759876
         }
      ],

      "picture":{
         "large":"https://randomuser.me/api/portraits/women/69.jpg",
         "medium":"https://randomuser.me/api/portraits/med/women/69.jpg",
         "thumbnail":https://randomuser.me/api/portraits/thumb/women/69.jpg
      },

      "playlists":[
         {
            "created":1420284570000,
            "id":"2331fec0-757d-4af9-963f-5cb0877e3b23",
            "name":"Playlist # 4 for Nellie",
            "updated":1441985973000,
            "visibility":"PRIVATE"
         },

         {
            "created":1428650768000,
            "id":"4313f4e1-4cc0-4502-9313-53a9f371ff62",
            "name":"Playlist # 3 for Nellie",
            "updated":1441985973000,
            "visibility":"PRIVATE"
         },

         {
            "created":1437099149000,
            "id":"ab723fc3-fd8f-4bde-99d0-d8f58353e631",
            "name":"Playlist # 5 for Nellie",
            "updated":1441985973000,
            "visibility":"PUBLIC"
         },

         {
            "created":1413115145000,
            "id":"d38221da-ac70-4c48-ae4c-1becb2773fd3",
            "name":"Playlist # 2 for Nellie",
            "updated":1441985973000,
            "visibility":"PRIVATE"
         },

         {
            "created":1430897658000,
            "id":"ff88b2b7-5afb-4b27-8aca-e7ad50e4ac7d",
            "name":"Playlist # 1 for Nellie",
            "updated":1441985973000,
            "visibility":"PUBLIC"
         }
      ],

      "pwd":"6962616e657a",
      "status":"active",
      "title":"Ms",
      "updated":1440516518000,
      "username":"abacusesthronging42801"
   }
}

Consider the ways in which playlists and tracks may be presented to the Couchify user. Playlists may be offered under two different scenarios; opening a playlist to play the songs and the former scenario where you wish to allow other users to browse public playlists. In the case of user opening a playlist, they may wish to review the details of the individual tracks as well. In this case, the playlist defined in the couchmusic2 will require a series of additional get() operations to fetch each of the track details. However, the playlist document defined in the couchmusic3 bucket embeds part of the track details.

{
  "playlist": {
    "created": 1428766518000,
    "id": "0002903e-e6f0-4e8f-94c0-18e80a19636f",
    "name": "Playlist # 1 for Kristen",
    "owner": {
      "created": 1419670543000,
      "firstName": "Kristen",
      "lastName": "Allen",

      "picture": {
        "large": "https://randomuser.me/api/portraits/women/69.jpg",
        "medium": "https://randomuser.me/api/portraits/med/women/69.jpg",
        "thumbnail": https://randomuser.me/api/portraits/thumb/women/69.jpg
      },

      "title": "Mrs",
      "updated": 1440516619000,
      "username": "vincibleplucky88570"
    },

    "tracks": [{
      "artist": "Wendy Carlos",
      "genre": "Experimental Electronic",
      "id": "31D4DBA236EE25926BCEF25F0A20E1D0B3C4410C",
      "mp3": "https://stream.song365.co/h/1276486/Wendy%20Carlos%20-%20Danny%20Bells%20Ascending%28From%20%22The%20Shining%22%29_(song365.cc).mp3",
      "title": "Danny Bells Ascending(From \"The Shining\")",
      "updated": 1439987185000
    }, {

      "artist": "Alvin Lucier",
      "genre": "Experimental Electronic",
      "id": "3E87B3ECD1C0FA060C58A144F813C833E6A1CD60",
      "mp3": "https://stream.song365.co/h/1076591/Alvin%20Lucier%20-%20Violin%20Solo_(song365.cc).mp3",
      "title": "Violin Solo",
      "updated": 1439860588000
    }, {

      "artist": "Nicolas Collins",
      "genre": "Experimental Electronic",
      "id": "CBF61EA93DA73ED39D454E0F8163147D5C99774A",
      "mp3": "https://stream.song365.co/h/367212/Nicolas%20Collins%20-%20Tobabo%20Fonio_(song365.cc).mp3",
      "title": "Tobabo Fonio",
      "updated": 1439943364000
    }, {

      "artist": "Harold Budd",
      "genre": "Experimental Electronic",
      "id": "5A0A98320000A10596B7C75B4ED4744092773EE4",
      "mp3": "https://stream.song365.co/h/675584/Harold%20Budd%20-%20Postlude_(song365.cc).mp3",
      "title": "Postlude",
      "updated": 1439917720000
    }, {

      "artist": "Peter Hammill",
      "genre": "Experimental Electronic",
      "id": "DFDD9CDD47DD0E3BD1C4FA4BA53D3CEFD253C0F2",
      "mp3": "https://stream.song365.co/h/176001/Peter%20Hammill%20-%20Golden%20Promises_(song365.cc).mp3",
      "title": "Golden Promises",
      "updated": 1439945848000
    }, {

      "artist": "Deuter",
      "genre": "Experimental Electronic",
      "id": "E1217FE25F736426E0CD612501D8C12083EFD78E",
      "mp3": "https://stream.song365.co/h/900860/Deuter%20-%20Buddha%20Nature_(song365.cc).mp3",
      "title": "Buddha Nature",
      "updated": 1439901148000
    }, {

      "artist": "Beaver \u0026 Krause",
      "genre": "Experimental Electronic",
      "id": "02D853E7E585375D851BBCF87B1BA7E8F93417B1",
      "mp3": "https://stream.song365.co/h/99948/Beaver%20%26%20Krause%20-%20Bluebird%20Canyon%20Stomp_(song365.cc).mp3",
      "title": "Bluebird Canyon Stomp",
      "updated": 1439866453000
    }],
    "updated": 1441986006000,
    "visibility": "PRIVATE"
  }
}

Creating domain objects for JSON documents

Use Case

As you saw with the prior lab, using domain objects in your application can make it a lot easier to perform code-related business logic. As a result, you will want to create domain objects to work with the other document types, which are the user profile and the track documents. Recall, from the prior lab that the way in which the Couchbase SDK manages the conversion of document to domain object and back is via a JsonSerializer, which handles the serialization/deserialization process. This presumes there is an adequate alignment between the definition of the domain object and the document being processed.

Take a moment to open each of the document types in the couchmusic2 bucket for the type track and userprofile. Note each of the attributes, the associated value and associated type. For simplicity, assume that the simple types will be one of string, int, float, DateTime. Note also where the attribute references a more complex object or Array. Make note of these as well because you will need to model these potentially as a separate domain object.

Performing the work

  1. In your IDE, open the the lab-05 project, and close unrelated projects.

  2. Expand the domain package. Note the addition of a number of additional domain objects. You will see empty classes for Address, PhoneDetails, RatingsDetails, Track and UserProfile.

  3. Open the Track.cs file in the _domain package and begin adding the properties to the class as private properties. Add mutator methods as well. If you notice one of the attributes refers to an object, look for a similar domain object in the domain package.

  4. Open the UserProfile.cs file and perform a similar task. This class has quite a few more attributes so make careful note of each one from the associated document.

  5. Validate you have completed updating each of the empty classes by running the Lab05Tests test.

    When you are able to run the tests without failures, you have succeeded in creating properly mapped domain objects.

Some things to think about

Based on the work you completed in defining domain objects in the prior step, here are some thing to think about.

  • What changes would be necessary to your domain objects if you were to add an additional attribute to the User Details document?

  • What would be the impact to the domain objects if a change were made to use the data model used in couchmusic3 rather than that in couchmusic2?

  • What are some alternative ways to represent the fields representing date and time?

Lab summary

In this lab you had a chance to become more familiar with the ways in which data related to the Couchify application can be modeled.

  • You looked at different ways in which the documents could be structured and saw clear differences between referencing other documents and partially or fully embedding data from related documents within the document in question.

  • You examined different scenarios that might dictate these different ways of modeling data.

  • Finally, you had a chance to define some domain objects based on the definition of document types found in the couchmusic2 bucket.

Congratulations. You have completed this lab.

results matching ""

    No results matching ""