HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Service-Repository pattern with IoC, NPoco micro-ORM and unit tests

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
microiocwithormtestsnpocoservicerepositoryandpattern

Problem

I'm trying to build a Web API using best practices, based on a simple proof-of-concept using students.

My goal is to build the system using:

  • Onion Architecture to seperate concerns.



  • Service-Repository for interaction between BLL and DAL.



  • Autofac for IoC, to control injected dependencies.



  • NPoco for micro-ORM.



  • Moq for mocking dependencies in unit tests.



One thing I'd like to add is a Unit Of Work using database transactions.

Please tell me what you think so I can expand my knowledge and get better.

Project is on GitHub.

Project consists of:

  • Common library containing DTO's and exceptions that are reused between BLL and API layer.



  • DAL library containing everything for data access: Entities, repositories and a database factory.



  • BLL library containing all services that the API layer uses.



  • API library containing everything for the HTTP layer: Controllers, IoC containers, etc.



DAL

Starting with the Student entity using NPoco filters/decorators:

[TableName("Students")]
public class Student
{
    [Column("ID")]
    public int ID { get; set; }
    [Column("Name")]
    public string Name { get; set; }
    [Column("Age")]
    public int Age { get; set; }
}


My interface for the Student Repository:

public interface IStudentRepository
{
    List GetAllStudents();
    Models.Entities.Student GetStudentByID(int id);
    void AddStudent(Models.Entities.Student student);
}


My DbFactory interface, to get the NPoco database connection object:

public interface IDbFactory
{
    IDatabase GetConnection();
}


My DbFactory implementation:

public class DbFactory : IDbFactory
{
    private readonly string _connectionString;

    public DbFactory(string connectionString)
    {
        _connectionString = connectionString;
    }

    public IDatabase GetConnection()
    {
        return new Database(_connectionString);
    }
}


My Student Repository, injected with the DbFactory:

```
public class StudentRepository : IStudentR

Solution

Initial and General Thoughts

Good job!

  • Built fine



  • Tests all passed



  • I like the project structure and naming



Source control - I find it easier to handle if packages not there, especially on a slow connection

  • Use nuget package restore



I generally use this .gitignore file for this type of project https://github.com/github/gitignore/blob/master/VisualStudio.gitignore

and to clear out files easily that are currently in a repo I'd use this method:
https://stackoverflow.com/questions/1139762/ignore-files-that-have-already-been-committed-to-a-git-repository

Can then run consolidate and update on packages (there is a newer version of NPoco.. from 2 to 3). Right click on solution and manage packages

I've put up my refactored code here:
https://bitbucket.org/davemateer/perfectpattern/overview

Pipeline

Continuous Integration - sanity check of a project. I generally always do this. Your code built fine first try on my machine - however this is not always the case. I use Appveyor as it is free for public repos. The only change I had to make from their default setup was:

in build section, before build script, appveyor-retry nuget restore
https://ci.appveyor.com/project/djhmateer/perfectpattern

it runs your tests (your project is running fine)

Code

organse and get rid of unused usings (alt e i o a)

removed unused references:
http://deanhume.com/home/blogpost/visual-studio---why-should-i-remove-unused-references-/6102
careful of the web project - tend to leave that alone

Student namespace

I recommend not having the namespace and the entity name the same:

was: namespace PerfectPattern.DAL.Models.Entities.Student
now: namespace PerfectPattern.DAL.Models.Entities

so, don't have to reference Models.Entities.Student everywhere

public interface IStudentRepository
{
    List GetAllStudents();
    Models.Entities.Student GetStudentByID(int id);
    void AddStudent(Models.Entities.Student student);
}


to

public interface IStudentRepository
{
    List GetAllStudents();
    Student GetStudentByID(int id);
    void AddStudent(Student student);
}


BLL/DAL and DTO's

So now this is pure opinion on how to do things, and there is no 'Perfect Pattern' :-) Every project has different needs. I have been trying recently using a flatter structure, essentially combining the BLL/DAL together. This negates the pass through code I see a lot in legacy BLL layers.

Regarding DTO's - I now use a ViewModel naming convention, and generally have 1 VM for each 'page' (if you were using a website):

  • studentDTO would become StudentHomePageViewModel



What is not there

Setup deployment to 'prod'. In your case I'd put up an azurewebsite.net site and get appveyor deploying there automatically. This would prove you can deploy to a prod environment. It also gets you thinking about database changes and how to handle it.

If you look at the output of this build project
https://ci.appveyor.com/project/djhmateer/booktech

You'll see I do a build, deploy an MSSQL database locally, run unit and integration tests, then deploy the database changes (as long as no data loss occurs) and code to Azure.

Database Project

I like the DB to be under source control.
eg https://bitbucket.org/davemateer/booktech/src

DB Profiling

I like MiniProfiler - helps me a lot in developing projects with significant data.

I generally use Dapper for micro ORMS.. interesting to see NPoco!

Closing thoughts / Architecture

I work in enterprise software, and generally make my software only as complex as it needs to be. Generally because it will last for many years, with many developers looking at it. I make this point because your codebase is easy to understand currently, so depending on the actual application, you may not want to add any other parts. In fact, using 'Pure DI' instead of an IoC would be a thought (contentious)! http://blog.ploeh.dk/2012/11/06/WhentouseaDIContainer/

Hope this helps.

Code Snippets

public interface IStudentRepository
{
    List<Models.Entities.Student> GetAllStudents();
    Models.Entities.Student GetStudentByID(int id);
    void AddStudent(Models.Entities.Student student);
}
public interface IStudentRepository
{
    List<Student> GetAllStudents();
    Student GetStudentByID(int id);
    void AddStudent(Student student);
}

Context

StackExchange Code Review Q#122010, answer score: 5

Revisions (0)

No revisions yet.