This section describes the data model in the surveys
application and explains how the table design partitions the data by
tenant.
The Surveys application uses a mix of table storage and BLOB storage to store its data. Figure 1 shows, at a high level, which data is stored in the different storage types.
Note:
The Surveys application uses BLOB and table storage.
1. Storing Survey Definitions
The Surveys application
stores the definition of surveys in two Windows Azure™ tables. This
section describes these tables and explains why Tailspin adopted this
design.
The following table describes the fields in the Surveys table. This table holds a list of all of the surveys in the application.
Field name | Notes |
---|
PartitionKey | This field contains the tenant name. Tailspin chose this value because they want to be able to filter quickly by tenant name. |
RowKey | This field contains the tenant name from the PartitionKey
field concatenated with the “slugified” version of the survey name.
This makes sure that a subscriber cannot create two surveys with the
same name. Two subscribers could choose the same name for their surveys. |
Timestamp | Windows Azure table storage automatically maintains the value in this field. |
SlugName | The “slugified” version of the survey name. |
CreatedOn | This field records when the subscriber created the survey. This will differ from the Timestamp value if the subscriber edits the survey. |
Title | The survey name. |
A slug
name is a string where all whitespace and invalid characters are
replaced with a hyphen (-). The term comes from the newsprint industry
and has nothing to do with those things in your garden! |
The following table describes the fields in the Questions table. The application uses this table to store the question definitions and to render a survey.
Field name | Notes |
---|
PartitionKey | This field contains the tenant name from the PartitionKey
field in the Surveys table concatenated with the “slugified” version of
the survey name. This enables the application to insert all questions
for a survey in a single transaction and to retrieve all the questions
in a survey quickly from a single partition. |
RowKey | This
field contains a formatted tick count concatenated with position of the
question within the survey. This guarantees a unique RowKey value and defines the ordering of the questions. |
Timestamp | Windows Azure table storage automatically maintains the value in this field. |
Text | The question text. |
Type | The question type: Simple text, multiple choice, or five stars (a numeric range). |
PossibleAnswers | This field contains a list of the possible answers if the question is a multiple-choice question. |
Remember that Windows Azure table storage only supports transactions within a single partition on a single table. |
2. Storing Tenant Data
The Surveys application saves tenant data in BLOB storage in a container named tenants. The following code shows the Tenant class; the application serializes Tenant
instances to BLOBs identified by the subscriber’s name (the term
“tenant” is used in the storage classes to refer to subscribers).
[Serializable]
public class Tenant
{
public string ClaimType { get; set; }
public string ClaimValue { get; set; }
public string HostGeoLocation { get; set; }
public string IssuerThumbPrint { get; set; }
public string IssuerUrl { get; set; }
public string Logo { get; set; }
public string Name { get; set; }
public string SqlAzureConnectionString { get; set; }
public string DatabaseName { get; set; }
public string DatabaseUserName { get; set; }
public string DatabasePassword { get; set; }
public string SqlAzureFirewallIpStart { get; set; }
public string SqlAzureFirewallIpEnd { get; set; }
}
The application collects most of the subscriber data during the on-boarding process. The Logo property contains the URL for the subscriber’s logo. The application stores logo images in a public BLOB container named logos.
3. Storing Survey Answers
The Surveys application
saves survey answers in BLOB storage. The application creates a BLOB
container for each survey with a name that follows this pattern:
surveyanswers-<tenant name>-<survey slug name>. This guarantees a unique container name for every survey.
For each completed survey
response, the Surveys application saves a BLOB into the survey’s
container. The BLOB name is a tick count derived from the current date
and time, which ensures that each BLOB in the container has a unique
name. The content of each BLOB is a SurveyAnswer object serialized in the JavaScript Object Notation (JSON) format. The following code example shows the SurveyAnswer and QuestionAnswer classes.
public class SurveyAnswer
{
...
public string SlugName { get; set; }
public string Tenant { get; set; }
public string Title { get; set; }
public DateTime CreatedOn { get; set; }
public List<QuestionAnswer> QuestionAnswers { get; set; }
}
public class QuestionAnswer
{
public string QuestionText { get; set; }
public QuestionType QuestionType { get; set; }
[Required(ErrorMessage = "* You must provide an answer.")]
public string Answer { get; set; }
public string PossibleAnswers { get; set; }
}
The Surveys application also uses BLOB storage to store an ordered list of the responses to each survey. For each survey, the application stores a BLOB that contains a serialized List object containing the ordered names of all the survey response BLOBs for that survey. The List object is serialized in the JSON format.
4. Storing Survey Answer Summaries
The Surveys application uses BLOB storage to save the summary statistical data for each survey. For each survey, it creates a BLOB named <tenant-name>-<survey slug name> in the surveyanswerssummaries container. The application serializes a SurveyAnswersSummary object in the JSON format to save the data. The following code example shows the SurveyAnswersSummary and QuestionAnswersSummary classes that define the summary data.
public class SurveyAnswersSummary
{
...
public string Tenant { get; set; }
public string SlugName { get; set; }
public int TotalAnswers { get; set; }
public List<QuestionAnswersSummary> QuestionAnswersSummaries
{ get; set; }
...
}
public class QuestionAnswersSummary
{
public string AnswersSummary { get; set; }
public QuestionType QuestionType { get; set; }
public string QuestionText { get; set; }
public string PossibleAnswers { get; set; }
}
Notice that the summary is
stored as a string for all question types, including numeric. This helps
to minimize the number of changes that would be required to add a new
question type to the Surveys application.
5. The Store Classes
The Surveys application uses store classes to manage storage. This section briefly outlines the responsibilities of each of these store classes.
5.1. SurveyStore Class
This class is responsible for saving survey definitions to table storage and retrieving the definitions from table storage.
5.2. SurveyAnswerStore Class
This class is responsible for saving survey
answers to BLOB storage and retrieving survey answers from BLOB
storage. This class creates a new container when it saves the first
response to a new survey. It uses a queue to track new survey responses;
the application uses this queue to calculate the summary statistical data for surveys.
This class also provides support for browsing sequentially through survey responses.
5.3. SurveyAnswersSummaryStore Class
This class is responsible for saving summary statistical data for surveys to BLOBs in the surveyanswerssummaries container, and for retrieving this data.
5.4. SurveySqlStore Class
This class is responsible for saving survey response data to SQL Azure.
5.5. SurveyTransferStore Class
This class is responsible placing a message on a queue when a subscriber requests the application to dump survey data to SQL Azure.
5.6. TenantStore Class
This class is
responsible for saving and retrieving subscriber data and saving
uploaded logo images. In the sample code, this class generates some
default data for the Adatum and Fabrikam subscribers.