Recently, I got challenged to code with following bullet points:
Extensible code to support different annual leave rules for HR departments
Maintainable code to add/change the existing rules without any impact on the other clients
Customizable and configurable code for different clients
Exception handling and logging to protect the code and also make support easier
Following design and OOP principles
Unit testable code
I coded keeping SOLID principle in mind and here is my code. What did I miss? I was not able to get a good score with it. What are the design strategies I am missing?
public class EmployeeLeave : ILeaveRequest
{
IDataBaseService databaseService;
public EmployeeLeave(IDataBaseService databaseService)
{
this.databaseService = databaseService;
}
public void ProcessLeaveRequest(DateTime leaveStartDate, int days, string reason, int employeeId)
{
try
{
var employee = FindEmployee(employeeId);
if ((DateTime.Now - employee.StartDate).TotalDays <= 90 && !employee.IsMarried)
throw new Exception("Invalid leave request.");
if (days > 20)
throw new Exception("Invalid leave request.");
var leaveRequest = new EmployeeLeaveDetail();
leaveRequest.EmployeeId = employeeId;
leaveRequest.LeaveStartDateTime = leaveStartDate;
leaveRequest.LeaveEndDateTime = leaveStartDate.AddDays(days);
SaveLeaveRequest(leaveRequest);
}
catch (Exception)
{
throw;
}
}
public void SaveLeaveRequest(ILeaveRequestDetail leaveRequest)
{
try
{
databaseService.ExecuteProcedure("spLocal_InsertIntoEmployees", new List<Parameters>
{
new Parameters {ParameterName="@EmployeeId",
ParameterValue =leaveRequest.EmployeeId },
new Parameters {ParameterName="@StartDate",
ParameterValue =leaveRequest.LeaveStartDateTime },
new Parameters {ParameterName="@EndDate",
ParameterValue =leaveRequest.LeaveEndDateTime }
});
}
catch (Exception)
{
throw;
}
}
public Employee FindEmployee(int employeeId)
{
Employee employee = default(Employee);
try
{
var sqlReader = databaseService.ExecuteProcedure("spLocal_GetEmployeeFromId", new List<Parameters> { new DBLayer.Parameters {
ParameterName="@EmployeeId",
ParameterValue=employeeId
} });
using (sqlReader)
{
while (sqlReader.Read())
{
employee = new Employee
{
EmployeeId = long.Parse(sqlReader["EmployeId"].ToString()),
Name = sqlReader["FirstName"].ToString(),
LastName = sqlReader["LastName"].ToString(),
StartDate = DateTime.Parse(sqlReader["StartDate"].ToString()),
Salary = Decimal.Parse(sqlReader["Salary"].ToString()),
IsMarried = bool.Parse(sqlReader["Married"].ToString())
};
}
}
}
catch (Exception)
{
throw;
}
return employee;
}
}
And below is my Unit Test.
public class AnualLeaveTest
{
[TestMethod]
public void IsMarriedAndGreaterThan90Days()
{
//TOdo: Need to check if connection ScopeIdentity returned is same
EmployeeLeave leave = new EmployeeLeave(new SQLDatabaseService());
leave.ProcessLeaveRequest(DateTime.Now, 6, "", 212);
}
[TestMethod]
public void IsMarriedAndLessThan90Days()
{
//To do: Need to check if connection ScopeIdentity returned is same
EmployeeLeave leave = new EmployeeLeave(new SQLDatabaseService());
leave.ProcessLeaveRequest(DateTime.Now, 6, "", 454);
}
[TestMethod]
public void NotMarriedAndGreaterThan90Days()
{
//TOdo: Need to check if connection ScopeIdentity returned is same
EmployeeLeave leave = new EmployeeLeave(new SQLDatabaseService());
leave.ProcessLeaveRequest(DateTime.Now, 6, "", 767);
}
[TestMethod]
[ExpectedException(typeof(Exception),"Invalid leave request.")]
public void NotMarriedAndLessThan90Days()
{
EmployeeLeave leave = new EmployeeLeave(new SQLDatabaseService());
leave.ProcessLeaveRequest(DateTime.Now, 6, "", 645);
}
[TestMethod]
[ExpectedException(typeof(Exception), "Invalid leave request.")]
public void LeaveRequestGreaterThan20Days()
{
EmployeeLeave leave = new EmployeeLeave(new SQLDatabaseService());
leave.ProcessLeaveRequest(DateTime.Now, 26, "", 645);
}
}