The document describes a .NET template solution architecture that is flexible, maintainable, sustainable, understandable, testable, and allows for easy addition of new features. It recommends layering the solution into projects for the frontend, services, data access, entities/DTOs, repositories, view models, inversion of control, and mapping entities to view models. Each layer has specific responsibilities and dependencies designed to separate concerns and enable loose coupling between layers.
2. Architecture should be
• Flexible to change, add and remove features
• Maintanable for many developers with different
coding habits
• Sustainable for growth
• Understandable for code review and optimization
• Easy to add new features with few lines of code
without losing structure
• Testable (unit and integration)
http://pt.linkedin.com/in/diogogcunha/ 2
4. Data LayerprojName.Data.dll
This layer would be a new Project inside the solution and it is an abstraction for the data
that the system writes and reads from different data sources. It should only contain CRUD
logic and nothing else.
Repositories and entity objects should be here.
proj.Data
http://pt.linkedin.com/in/diogogcunha/ 4
5. Entities/DTOsprojName.Data.Entities
• Entities are the raw domain objects that come from the data source. So if you are
integrating with and external platform such as Facebook or if you are writing on a XML
you should have that information in object classes so you have strongly typed entities.
• A lot of .Net developers will use EntityFramework to do most of the work writing to the
database. Using model first this is the place to put the .edmx file, using code first this is
where you’ll put your DbContext file and your entities.
• In order to be able to mock the DbContext or the ObjectContext you should do a
wrapper around it (or a partial class) use an interface and expose what you need.
• Avoid unecessary dependecies using different projects under the same namespace.
UserEntity FileEntity PostEntity
UserRepository FileRepository FacebookPostRepository
UserEntity FileEntity PostEntity
5
projName.Data.dll projName.Data.Facebook.dll
6. Repositories
projName.Data.Repositories
• Each entity should have it’s own repository. If the entity is read only so should be the
repository.
• All repositories must have their own interface and it might be useful to have abstract
repositories to decrease the amount of code to be written.
• Repository methods should be easily overriden for flexibility so we could make them
virtual but it’s not mandatory because C# let’s you override a method with the [new]
word on the function signature.
abstract class BaseRepository : IBaseRepository
abstract class ReadRepository<T> : BaseRepository, IReadRepository<T>
abstract class WriteRepository<T> : ReadRepository<T>, IWriteRepository<T>
WritableEntityRepository : WriteRepository<WritableEntity>, IWritableEntityRepository
ReadOnlyEntityRepository : ReadRepository<ReadOnlyEntity>, IReadOnlyEntityRepository
http://pt.linkedin.com/in/diogogcunha/ 6
7. Repositories
projName.Data.Repositories.ReadRepository
• Read repository is probably the most used one, so we should try to make it as powerfull
as possible. Also because LINQ is cool I’m copying some of it’s namings.
public interface IReadRepository<T> where T : class {
T FirstOrDefault(Expression<Func<T, bool>> predicate);
IEnumerable<T> GetAll(Expression<Func<T, bool>> predicate);
IEnumerable<T> GetAll(Expression<Func<T, bool>> predicate,
Expression<Func<T, object>> orderBy,
bool descending = false);
int Count(Expression<Func<T, bool>> predicate);
bool Any(Expression<Func<T, bool>> predicate);
}
http://pt.linkedin.com/in/diogogcunha/ 7
8. Repositories
projName.Data.Repositories.WriteRepository
• Creating and updating entities is usualy a fairly simple operation and it should remain
so. No other kind of logic should be implemented in these classes except default values
like , for instance a CreationDate = DateTime.Now;
• In some situations the Update method is not necessary (if you use EntityFramework for
some of your data) so don’t feel that obligated to implement this method, just leave the
possibility there for other data sources that might need it.
public interface IWriteRepository<T> where T : class {
T Add(T entity);
T Update(T entity);
T Remove(T entity);
}
http://pt.linkedin.com/in/diogogcunha/ 8
9. Services Layer
projName.Services.dll
• This layer would be a new Project inside the solution that references the Data project
and it is where all the business logic should be centralized.
• Services should be divided by actions oriented and also reading services or writing
services, this will allow all writing services to be dependent on their corresponding
reading services if needed (example: instead of a UserService use UserInfoService,
UserEditService and UserAuthService)
• External modules should be added to avoid unwanted dependencies to the main .dll file
Services
ProductServices UserServices Services.HttpServices
getProductDiscount() addLoginCount()
ProductEditService UserEditService UserSessionService
ProductInfoService UserInfoService projName.Services.HttpServices.dll
getUser() dependent on System.Web
9
10. Services Layer
projName.Services
public class UserInfoService : UnitOfWorkService, IUserInfoService {
public UserInfoService(IUnitOfWork unitOfWork) : base(unitOfWork) { }
}
public class UserEditService : UnitOfWorkService, IUserInfoService {
IUserInfoService UserInfoService { get; set; }
public UserEditService(IUnitOfWork unitOfWork, IUserInfoService userInfoSvc)
: base(unitOfWork) {
UserInfoService = userInfoSvc;
}
}
public class UserSessionService : BaseService, IUserSessionService {
IUserInfoService UserInfoService { get; set; }
IUserEditService UserEditService { get; set; }
public UserSessionService(IUserInfoService userInfoSvc,
IUserEditService userEditSvc) {
UserEditService = userEditService;
UserInfoService = userInfoService;
}
}
http://pt.linkedin.com/in/diogogcunha/ 10
11. Services Layer and Data Layer
projName.Data.UnitOfWork.dll
• A service will use the Unit of Work to access the data layer (the Unit of Work pattern is a
class that has a reference to all the repositories and to the context in which these
repositories work giving only access to the repositories and a SaveChanges method that
commits the changes to the database). This doesn’t need to be a separate Project.
• This should be implemented on a different Project so that when you reference the
Services on the Frontend you don’t have access to the Unit of Work.
//namespace projName.Services
abstract class UnitOfWorkService : BaseService, IUnitOfWorkService {
private IUnitOfWork UnitOfWork { get; set; }
public UnitOfWorkService(IUnitOfWork unitOfWork){
UnitOfWork = unitOfWork;
}
}
//namespace projName.Data.UnitOfWork
public interface IUnitOfWork {
void SaveChanges();
public IUserRepository UserRepository { get; set; }
public IProductsRepository ProductsRepository { get; set; }
}
http://pt.linkedin.com/in/diogogcunha/ 11
12. Mapping Entity <-> ViewModel
projName.Services.Mappings
• This is probably the most boring code to write because it’s simply transforming one
object to another one, so I usually use AutoMapper which is a very handy tool.
• There are several ways to do these mappings and I believe that the big concern here is
performance and easily understand to which ViewModels does a Entity map to and how
that mapping is processed, and the other way arround.
UserEntity UserViewModel
-ID -UserIdentityViewModel
-Username -ID
-Username
-FirstName
-LastName
Mapping engine -Email
-Email -UserInfoViewModel
-Gender -FirstName
-CreationDate -LastName
-Gender
http://pt.linkedin.com/in/diogogcunha/ 12
13. ViewModels
projName.ViewModels.dll
• The view models should be another Project in the solution to be referenced by the
services and Frontend.
• Services only receive and return ViewModel objects that should have Frontend needs in
mind and not domain entities to make them aligned with the operations they refer to.
• Dividing the ViewModels into folders according to the entities they refer to will make
the code more maintainable.
Frontend
View View
Models Services Models
http://pt.linkedin.com/in/diogogcunha/ 13
14. Inversion of Control
projName.IoC.dll
• A separate Project should be made for the IoC (even if it only has one file with all the
class registrations) because it must reference the Services Layer and the Data Layer.
• Inversion of Control pattern can save you a lot of code lines, help you keep things
modular and improve performance.
• It’s not mandatory to use it for this architecture to work but it is as advantage.
• We can initialize a service in 3 different ways with this architecture:
public class UserController : Controller {
private IUserInfoService _userInfoService { get; set; }
public UserInfoService(IUserInfoService userInfoService)
{
_userInfoService = userInfoservice;//with Dependency Injection
_userInfoService = new UserInfoService(IoC.Locator<IUnitOfWork>());//with Locator
_userInfoService = new UserInfoService(new UnitOfWork());//NO IoC
}
}
http://pt.linkedin.com/in/diogogcunha/ 14
15. Frontend layer
projName.Frontend.dll
• Frontend layer is where your services actually get exposed in whatever way you want.
• It should be as easy to use a MVC.Net project on top of this architecture as it would be
to use WebForms, WinForms or a Mobile App.
Services
http://pt.linkedin.com/in/diogogcunha/ 15