After doing a whole bunch of research on Onion Architecture, I have made an attempt at implementing this in a new system that we are developing.We have the Layers as per below:DomainDomain.EntitiesDomain.InterfacesInfrastructureInfrastructure.DataInfrastructure.DependencyResolutionInfrastructure.InterfacesInfrastructure.LoggingServicesServices.InterfacesTestsTests.CoreWebWeb.UIDomain.Entities - All the domain models are kept here.Base Entity namespace Domain.Entities {    public class BaseEntity    {        public virtual Guid ID { get; set; }        public virtual DateTime DateCreated { get; set; }        public virtual DateTime? DeletedDate { get; set; }         public override bool Equals (object obj)        {            if (obj == null)                return false;              var t = obj as BaseEntity;              if (t == null)                return false;              if (ID == t.ID)                return true;              return false;          }        public override int GetHashCode ()        {            int hash = GetType ().GetHashCode ();            hash = (hash * 397) ^ ID.GetHashCode ();            return hash;        }    }}Company Entitynamespace Domain.Entities{    public class Company : BaseEntity    {        public virtual string Name { get; set; }        public virtual IEnumerable<User> Users { get; set; }        public virtual IEnumerable<Branch> Branches { get; set; }        public virtual IEnumerable<Department> Departments { get; set; }        public class CompanyCollection : List<Company>        {        }    }}Domain.Interfaces - I have a bunch of generic interfaces programmed here.IReadRepositoryIReadWriteRepsitoryIUnitOfWorkIWriteRepositoryIReadRepositorynamespace Domain.Interfaces{    public interface IReadRepository<TEntity> where TEntity : class    {        IQueryable<TEntity> All ();        TEntity FindBy (Expression<Func<TEntity, bool>> expression);        TEntity FindBy (object id);        IQueryable<TEntity> FilterBy (Expression<Func<TEntity, bool>> expression);    }}IWriteRepositorynamespace Domain.Interfaces{    public interface IWriteRepository<TEntity> where TEntity : class    {        bool Add (TEntity entity);        bool Add (IEnumerable<TEntity> entities);        bool Update (TEntity entity);        bool Update (IEnumerable<TEntity> entities);        bool Delete (TEntity entity);        bool Delete (IEnumerable<TEntity> entities);    }}IReadWriteRepositorynamespace Domain.Interfaces{    public interface IReadWriteRepository<TEntity> :                         IReadRepository<TEntity>, IWriteRepository<TEntity> where TEntity : class    {    }}IUnitOfWorknamespace Domain.Interfaces{    public interface IUnitOfWork : IDisposable    {        void Commit ();        void Rollback ();    }}Infrastructure.Interfaces - Store all infrastructure service interfaces herenamespace Infrastructure.Interfaces{    public interface IConfigService    {        string Connection{ get; }    }}namespace Infrastructure.Interfaces{    public interface ILoggingService    {        bool IsDebugEnabled { get; }        bool IsErrorEnabled { get; }        bool IsFatalEnabled { get; }        bool IsInfoEnabled { get; }        bool IsTraceEnabled { get; }        bool IsWarnEnabled { get; }        void Debug (Exception exception);        void Debug (string format, params object[] args);        void Debug (Exception exception, string format, params object[] args);        void Error (Exception exception);        void Error (string format, params object[] args);        void Error (Exception exception, string format, params object[] args);        void Fatal (Exception exception);        void Fatal (string format, params object[] args);        void Fatal (Exception exception, string format, params object[] args);        void Info (Exception exception);        void Info (string format, params object[] args);        void Info (Exception exception, string format, params object[] args);        void Trace (Exception exception);        void Trace (string format, params object[] args);        void Trace (Exception exception, string format, params object[] args);        void Warn (Exception exception);        void Warn (string format, params object[] args);        void Warn (Exception exception, string format, params object[] args);    }}Infrastructure.Data - I'm using Fluent NHibernate to talk to a postgresql database.In here I keep my Mapping Files, Unit of work implementation and the Generic repository implementation.Hibernate Helpernamespace Infrastructure.Data.Helpers{    public class NHibernateHelper    {        private ISessionFactory _sessionFactory;        private readonly string _connectionString;        public NHibernateHelper (string connectionString)        {            if (string.IsNullOrEmpty (connectionString))                throw new HibernateConfigException (ConnectionString in Web.config is not set.);            _connectionString = connectionString;        }        public ISessionFactory SessionFactory {            get {                return _sessionFactory ?? (_sessionFactory = InitializeSessionFactory ());            }        }        private ISessionFactory InitializeSessionFactory ()        {            return Fluently.Configure () . . . . . . .;        }    }}Unit Of Worknamespace Infrastructure.Data.Helpers{    public class UnitOfWork : IUnitOfWork    {        private readonly ISessionFactory _sessionFactory;        private readonly ITransaction _transaction;        public ISession Session { get; private set; }        public UnitOfWork (ISessionFactory sessionFactory)        {            _sessionFactory = sessionFactory;            Session = _sessionFactory.OpenSession ();            Session.FlushMode = FlushMode.Auto;            _transaction = Session.BeginTransaction (IsolationLevel.ReadCommitted);        }        public void Commit ()        {            if (!_transaction.IsActive) {                throw new InvalidOperationException (Oops! We don't have an active transaction);            }            _transaction.Commit ();        }        public void Rollback ()        {            if (_transaction.IsActive) {                _transaction.Rollback ();            }        }        public void Dispose ()        {            if (Session.IsOpen) {                Session.Close ();                Session = null;            }        }    }}Base Mapping namespace Infrastructure.Data.Mapping{    public class BaseMapping : ClassMap<BaseEntity>    {        public BaseMapping ()        {            UseUnionSubclassForInheritanceMapping ();            Id (x => x.ID);            Map (x => x.DateCreated);            Map (x => x.DeletedDate);        }    }}Company Mappingnamespace Infrastructure.Data.Mapping{    public class CompanyMapping : SubclassMap<Company>    {        public CompanyMapping ()        {            Abstract ();            Map (x => x.Name);            HasManyToMany<User> (x => x.Users).Table (CompanyUser).Inverse ();            HasMany<Branch> (x => x.Branches).Inverse ().Cascade.All ();            HasMany<Department> (x => x.Departments).Inverse ().Cascade.All ();            Table (Company);        }    }}Repository Implementationnamespace Infrastructure.Data.Repositories{    public class Repository<TEntity> : IReadWriteRepository<TEntity>        where TEntity : class    {        private readonly ISession _session;        public Repository (ISession session)        {            _session = session;        }        #region IWriteRepository        public bool Add (TEntity entity)        {            _session.Save (entity);            return true;        }        public bool Add (System.Collections.Generic.IEnumerable<TEntity> entities)        {            foreach (TEntity entity in entities) {                _session.Save (entity);            }            return true;        }        public bool Update (TEntity entity)        {            _session.Update (entity);            return true;        }        public bool Update (System.Collections.Generic.IEnumerable<TEntity> entities)        {            foreach (TEntity entity in entities) {                _session.Update (entity);            }            return true;        }        public bool Delete (TEntity entity)        {            _session.Delete (entity);            return true;        }        public bool Delete (System.Collections.Generic.IEnumerable<TEntity> entities)        {            foreach (TEntity entity in entities) {                _session.Delete (entity);            }            return true;        }        #endregion        #region IReadRepository        public System.Linq.IQueryable<TEntity> All ()        {            return _session.Query<TEntity> ();        }        public TEntity FindBy (System.Linq.Expressions.Expression<System.Func<TEntity, bool>> expression)        {            return FilterBy (expression).SingleOrDefault ();        }        public TEntity FindBy (object id)        {            return _session.Get<TEntity> (id);        }        public System.Linq.IQueryable<TEntity> FilterBy (System.Linq.Expressions.Expression<System.Func<TEntity, bool>> expression)        {            return All ().Where (expression).AsQueryable ();        }        #endregion    }}Finally, the service for dependency injection:namespace Infrastructure.Data.Services{    public class ConfigService : IConfigService    {        #region IConfigService implementation        public string Connection {            get {                string strConnectionString = null;                var connectionSettings = ConfigurationManager.ConnectionStrings [Connection];                if (connectionSettings != null) {                    strConnectionString = connectionSettings.ConnectionString;                }                return strConnectionString;            }        }        #endregion    }}Infrastructure.DependencyResolution - Using Simple InjectorIn here I store all my registration packages / modules ConfigPackagenamespace Infrastructure.DependecyResolution{    public class ConfigPackage : IPackage    {        #region IPackage implementation        public void RegisterServices (SimpleInjector.Container container)        {            container.Register<IConfigService,ConfigService> ();        }        #endregion    }}RepositoryPackagenamespace Infrastructure.DependecyResolution{    public class RepositoryPackage : IPackage    {        #region IPackage implementation        public void RegisterServices (SimpleInjector.Container container)        {            container.RegisterPerWebRequest<ISessionFactory> (() => {                var configPackage = container.GetInstance<IConfigService> ();                NHibernateHelper objNHibernate = new NHibernateHelper (configPackage.Connection);                return objNHibernate.SessionFactory;            });            container.RegisterPerWebRequest<IUnitOfWork, UnitOfWork> ();            container.RegisterPerWebRequest<ISession> (() => {                UnitOfWork unitOfWork = (UnitOfWork)container.GetInstance<IUnitOfWork> ();                return unitOfWork.Session;            });            container.RegisterOpenGeneric (typeof(IReadWriteRepository<>), typeof(Repository<>));        }        #endregion    }}LoggingPackagenamespace Infrastructure.DependecyResolution{    public class LoggingPackage : IPackage    {        #region IPackage implementation        public void RegisterServices (SimpleInjector.Container container)        {            ILoggingService logger = GetLoggingService ();            container.Register<ILoggingService> (() => logger);        }        #endregion        private ILoggingService GetLoggingService ()        {            ConfigurationItemFactory.Default.LayoutRenderers.RegisterDefinition (utc_date, typeof(UtcDateRenderer));            ConfigurationItemFactory.Default.LayoutRenderers.RegisterDefinition (web_variables, typeof(WebVariablesRenderer));            ILoggingService logger = (ILoggingService)LogManager.GetLogger (NLogLogger, typeof(LoggingService));            return logger;        }    }}Infrastructure.Logging - using NLogIn here is just the implementation of the logging interface.Services.Interfaces - Applicaiton Service interfaces gets stored herenamespace Services.Interfaces{    public interface ICompanyService    {        IQueryable<Company> GetCompanies ();        Company GetCompany (Guid guidId);        void CreateNewCompany (Company company);        void UpdateExistingCompany (Company company);        void DeleteCompany (Company company);    }}Tests.CoreRepository and Service Tests are in here at this stage, sure there will be more tests going in here.Web.UI - ASP.NET MVC 4 Project as user interface applicationImplementation of the Company Service Interface.namespace Web.UI.Services{    public class CompanyService : ICompanyService    {        private IUnitOfWork _unitOfWork;        private IReadWriteRepository<Company> _companyRepository;        public CompanyService (IUnitOfWork unitOfWork, IReadWriteRepository<Company> companyRepository)        {            _unitOfWork = unitOfWork;            _companyRepository = companyRepository;        }        #region ICompanyService implementation        public IQueryable<Company> GetCompanies ()        {            return _companyRepository.All ().Where (x => !x.DeletedDate.HasValue);        }        public Company GetCompany (Guid guidId)        {            return _companyRepository.FindBy (guidId);        }        public void CreateNewCompany (Company company)        {            if (_companyRepository.Add (company))                _unitOfWork.Commit ();            else                _unitOfWork.Rollback ();        }        public void UpdateExistingCompany (Company company)        {            if (_companyRepository.Update (company))                _unitOfWork.Commit ();            else                _unitOfWork.Rollback ();        }        public void DeleteCompany (Company company)        {            if (_companyRepository.Update (company))                _unitOfWork.Commit ();            else                _unitOfWork.Rollback ();        }        #endregion    }}The Inject the ICompanyService into the Controller. Create a view model for Company which the view can manipulate. Simple Injector Service also gets initialized in this project. While it gets initialized I register the Services.Interfaces to Web.UI.Services implementation.That is pretty much how I have implemented the Onion Architecture. Let me know if I went wrong somewhere. This is the first time I set up an application with a proper architecture.
Onion Architecture
c#;design patterns;asp.net mvc 4;repository;hibernate
Let me know if I went wrong somewhere. This is the first time I set up an application with a proper architecture.I'm afraid there is no such thing as general proper architecture. Relevant architecture is the one that enables/assists developers in implementing new functionality or adjusting your solution to new requirements.In your implementation I don't see the reason to define your own logging and repository/UoW patterns:logging - there is NLog/log4net/whatever, why abstract from them? Even if you may want to switch the implementation, use Common.Logging. All these frameworks do not (and should not) use dependency injection, so having ILoggingService injected will only make your life harder.repository/UnitOfWork pattern - It's a long-running discussion between those who think repository is a must, and those who see it as a redundant layer that only makes your life harder, and leads to leaked abstractions in all but most simple scenarios. I'm in the latter camp, so here are a couple of links from one of the NHibernate core devs: Repository is the new SingletonRefactoring toward frictionless & odorless code: The baselineRefactoring toward frictionless & odorless code: Hiding global stateRefactoring toward frictionless & odorless code: Limiting session scopeRefactoring toward frictionless & odorless code: A broken home (controller)Refactoring toward frictionless & odorless code: The case for the view modelRefactoring toward frictionless & odorless code: Getting rid of globalsRefactoring toward frictionless & odorless code: What about transactions?Note that the series of articles Refactoring toward frictionless & odorless code actually show how you can move the transaction/session management to infrastructure level, leaving your business logic clean an tidy.And a couple of small comments:I don't see any reason to have public class CompanyCollection : List<Company> {}Repository - why do you return true in Update methods? If there is a failure - throw exception, not return false.IConfigService/ConfigService breaks Open/Closed principle, as would need to edit this class (add properties) whenever a new configuration parameter is needed. Classes that require configuration should expect specific configuration values in constructor instead of consuming the general IConfigService. Inject these parameters at the DI registration time. If you need a dynamic configuration which can be changed during software execution - then some sort of IConfigService can be implemented, but I would suggest to have a general T GetValue<T>(string configName) in this case.