Programming tutorials shows us a land of promise where everything happens as you think; as soon as you think. But real world doesn't work that way most of the times. Here; you spend hours debugging some CORS error or thinking why your database table Id column is not auto-incrementing. For the last 2 days; I am participating in a coding interview which spans 2 days and these series of blog is based on that experience - what am I thinking at each stage; what is the issue and how I am resolving them.
Project Requirement
I need to design a survey system. It will have 3 main screen.
- Admin will create an user. User email will be unique. User will also have name, mobile, isActive field.
- Admin will create a survey. For simplicity, a survey will have only 3 questions. Each questions will have 4 answers. only one answer can be selected as correct answer. Each question will have one and only one correct answer.
- User can participate in the survey with a link from email.
The flow would be like this - User create page with user list in the bottom (with number of surveys participated) -> survey creation page -> survey list page with option to send invitation to all ACTIVE users -> user participating in survey. After creating a survey; in the survey list there will be option to send emails to each user with unique link (unique to each user for each survey) to take the survey.
Requirement and validations:
- Using Dot-net core (not webform or anything)
- Using some sort of front end technology (angular/react)
- Using entity framework
- Each survey questions will have only one answer
- Proper front end validation
- In user page; show count of surveys that an user participated
- Implement some sort of email gateway to send mails
My Initial Thinking Process
I have assumed few things that have proved costly (or wrong) down the road. For example:
I should have picked code first or database first approach.
I picked neither.
And I thought I would just create the table with SQL and bind them with rest Api project.
BIG MISTAKE.
Why?
Because then comes my second mistake. As I am not using migrations; I assumed I could just pass Entities object as DTO objects and cuts out the auto mapping portion.
But that didn't work very well.
I didn't think about making surveys dynamic with number of questions; and for that reason my submission table is stuck with 3 answers.
Designing Database
My initial db design has 6 tables - user, survey, question, answer, invitation, submission.
And my create table script is like below:
CREATE TABLE [user]
(
id INT PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
mobile VARCHAR(15) NOT NULL,
isActive BIT NOT NULL,
role VARCHAR(255) NOT NULL
);
CREATE TABLE survey
(
id INT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
created_date DATETIME NOT NULL,
updated_date DATETIME NOT NULL
);
CREATE TABLE question
(
id INT PRIMARY KEY,
survey_id INT NOT NULL,
text NVARCHAR(MAX) NOT NULL,
FOREIGN KEY (survey_id) REFERENCES survey(id)
);
CREATE TABLE answer
(
id INT PRIMARY KEY,
question_id INT NOT NULL,
text NVARCHAR(MAX) NOT NULL,
is_correct BIT NOT NULL,
FOREIGN KEY (question_id) REFERENCES question(id)
);
CREATE TABLE invitation
(
id INT PRIMARY KEY,
survey_id INT NOT NULL,
user_id INT NOT NULL,
invitation_link NVARCHAR(MAX) NOT NULL,
FOREIGN KEY (survey_id) REFERENCES survey(id),
FOREIGN KEY (user_id) REFERENCES [user](id)
);
CREATE TABLE submission
(
id INT PRIMARY KEY,
survey_id INT NOT NULL,
user_id INT NOT NULL,
answer1 INT NOT NULL,
answer2 INT NOT NULL,
answer3 INT NOT NULL,
score INT NOT NULL,
FOREIGN KEY (survey_id) REFERENCES survey(id),
FOREIGN KEY (user_id) REFERENCES [user](id)
);
Can you spot any issues with this query? (Hint: not setting any identity was a huge pain in the - "ahem" - heart)
Creating Backend Project
Alongside this; I also created the backend project. From visual studio; go to create new project -> ASP.NET core web api -> an create the app (I left authentication unchecked; another mistake; it should be fairly easy to implement and almost must do for any sort of real life project) and kept the weather forecast controller. After adding services and repositories folder; the folder structure looked like this:
I then proceeded to add the required DTOs (and entities also; as I thought re-using them would be so easy!)
public int Id { get; set; }
public int SurveyId { get; set; }
public int UserId { get; set; }
public int Answer1 { get; set; }
public int Answer2 { get; set; }
public int Answer3 { get; set; }
public int Score { get; set; }
namespace dev_test.DTOs
{
public class Survey
{
public int Id { get; set; }
public string Title { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime UpdatedDate { get; set; }
}
}
namespace dev_test.DTOs
{
public class Question
{
public int Id { get; set; }
public int SurveyId { get; set; }
public string Text { get; set; }
}
}
namespace dev_test.DTOs
{
public class Answer
{
public int Id { get; set; }
public int QuestionId { get; set; }
public string Text { get; set; }
public bool IsCorrect { get; set; }
}
}
namespace dev_test.DTOs
{
public class Invitation
{
public int Id { get; set; }
public int SurveyId { get; set; }
public int UserId { get; set; }
public string InvitationLink { get; set; }
}
}
namespace dev_test.DTOs
{
public class Submission
{
public int Id { get; set; }
public int SurveyId { get; set; }
public int UserId { get; set; }
public int Answer1 { get; set; }
public int Answer2 { get; set; }
public int Answer3 { get; set; }
public int Score { get; set; }
}
}
The thinking process behind Submission class is that as there would only be 3 questions (from requirement); I should just save all the submission data in one class (another mistake; it stopped the system to made any change so much difficult!)
Adding Controllers
Then I added 2 controllers - UserController and SurveyController with read/write methods (deleted edit and delete methods as they were out of scope). But designing SurveyController posed an issue. As survey is not only survey table; but it has some questions and answers; it was interesting how to receive data from the front-end. I thought about creating a composite object to receive as I decided to use reactive form in the front end with angular. I also added 2 repository with their interface and 2 services with their interfaces for user and survey. These classes are as follows:
namespace dev_test.DTOs
{
public class QuestionComposite // dto
{
public int SurveyId { get; set; }
public string Text { get; set; }
public List<Answer> Answers { get; set; }
}
}
namespace dev_test.DTOs
{
public class SurveyComposite // dto
{
public string Title { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime UpdatedDate { get; set; }
public List<Question> Questions { get; set; }
}
}
using dev_test.DTOs;
namespace dev_test.Repositories.Contracts // repository interface
{
public interface IUserRepository
{
public IEnumerable<User> GetUsers();
public void PostUser(User user);
}
}
using dev_test.DTOs;
namespace dev_test.Repositories.Contracts // repository interface
{
public interface ISurveyRepository
{
public IEnumerable<SurveyComposite> GetSurveys();
public void PostSurveys(SurveyComposite survey);
}
}
using dev_test.DTOs;
namespace dev_test.Services.Contracts // service interface
{
public interface IUserService
{
public IEnumerable<User> GetUsers();
public void PostUser(User user);
}
}
using dev_test.DTOs;
namespace dev_test.Services.Contracts // service interface
{
public interface ISurveyService
{
public IEnumerable<SurveyComposite> GetSurveys();
public void PostSurveys(SurveyComposite survey);
}
}
If you are using Visual Studio 2022; you might be seeing some warning here and there. Pay close attention to those. They are trying to tell something.
I then thought about writing the solid classes for the repo and service contracts; but then I remembered I don't have any way to connect to db or dbcontext or something like that. So I decided to add that to the project. (Will be continued ...)
Other episodes in this series:
First Part
Second Part
Third Part
Fourth Part
And the code is given is updated into -
Github
Top comments (2)
An Awesome read indeed👍. I realized that this article is a part in a series, you might want to check out this guide on how create a series on DEV, as it would help curate the articles in the series in a more organized and chronological order for your readers.
Create a "Series" from Markdown Editor
Joel Louzado ・ Feb 2 '21 ・ 2 min read
Thank you very much. I would definitely check it out and modify my series accordingly.