applied patterns in the project
TRANSCRIPT
APPLIED PATTERNSThe current project implementation
TOPICS SOLID Repository pattern Unit of Work pattern Decorator pattern
SOLID Single Responsibility Principle
“a class should have only a single responsibility” Open/closed Principle
“software entities … should be open for extension, but closed for modification.”
Liskov substitution principle “objects in a program should be replaceable with
instances of their subtypes without altering the correctness of that program.”
Interface segregation principle ““many client-specific interfaces are better than one
general-purpose interface.” Dependency inversion principle
one should “Depend upon Abstractions. Do not depend upon concretions.”
REPOSITORY PATTERN Abstraction data access
Database Services Files Etc.
Hide implementation details
SEPERATION Repository.Database
Database context Initializers Migrations Scripts
Repository.Entities Entity objects (POCO)
Repository.DataAccess Repository abstraction layer
REPOSITORY INTERFACEpublic interface IRepository<TEntity>
where TEntity : class{
IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null,Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "");TEntity GetById(object id);TEntity Insert(TEntity entity);TEntity Delete(object id);TEntity Delete(TEntity entityToDelete);void Update(TEntity entityToUpdate);
}
REPOSITORY IMPLEMENTATIONpublic Repository(DatabaseContext context){
this.context = context;
this.dbSet = context.Set<TEntity>();}
DI REGISTRATIONbuilder.RegisterGeneric(typeof(Repository<>))
.As(typeof(IRepository<>));
builder.RegisterType<DatabaseContext>()
.AsSelf()
.InstancePerLifetimeScope();
USAGEprotected StoreNewQueueItem( IRepository<ProcessQueue> processQueueRepository, ...){ this.processQueueRepository = processQueueRepository; ...}
...var insertedItem = processQueueRepository .Insert(queueItem);...
UNIT OF WORK PATTERN “A Unit of Work keeps track of everything
you do during a business transaction that can affect the database. When you're done, it figures out everything that needs to be done to alter the database as a result of your work.”SOURCE: http://www.martinfowler.com/eaaCatalog/unitOfWork.html
Committing changes Handling of transaction
COMMON REPOSITORY USAGEvar insertedItem = processQueueRepository .Insert(queueItem);processQueueRepository.Save();
OR
public virtual TEntity Insert( TEntity entity){ var insertedItem = dbSet.Add(entity); context.SaveChanges(); return insertedItem;}
UNIT OF WORK + REPOSITORYpublic class MyCommand{
private readonly IUnitOfWorkFactory factory; public MyCommand(IUnitOfWorkFactory factory)
{this.factory = factory;
} public void Execute()
{using (var context = this.factory.CreateNew()){
this.DoSomeNiceThings(context); context.Commit();
}}
}
SOURCE: http://stackoverflow.com/a/4944201/352640
NOT A GOOD IMPLEMENTATION UoW + Repository, great idea Some of the greater developer minds are
opponents Violates some principles
Opaque dependencies Open/closed principle Single Responsibility Principle Nominal abstraction
BETTER IMPLEMENTATION? List of example implementations:
SOURCE: https://lostechies.com/derekgreer/2015/11/01/survey-of-entity-framework-unit-of-work-patterns/
Unit of Work Decorator Works everywhere Close to point of need More control Setup is hard!
DECORATOR PATTERN “…the decorator pattern … allows behavior
to be added to an individual object … without affecting the behavior of other objects from the same class. The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern.”SOURCE: https://en.wikipedia.org/wiki/Decorator_pattern
Extending functionality Adhering SRP
DECORATOR PATTERN CLASS DIAGRAM Decorator implements same interface
DECORATOR PATTERN EXAMPLE/// <summary>/// The 'Component' abstract class/// </summary>abstract class Component{ public abstract void Operation();} /// <summary>/// The 'Decorator' abstract class/// </summary>abstract class Decorator : Component{ protected Component component; public void SetComponent(Component component) { this.component = component; } public override void Operation() { if (component != null) { component.Operation(); } }}static void Main(){ // Create ConcreteComponent and two Decorators ConcreteComponent c = new ConcreteComponent(); ConcreteDecoratorA d1 = new ConcreteDecoratorA(); ConcreteDecoratorB d2 = new ConcreteDecoratorB(); // Link decorators d1.SetComponent(c); d2.SetComponent(d1); d2.Operation();}
/// <summary>/// The 'ConcreteComponent' class/// </summary>class ConcreteComponent : Component{ public override void Operation() { Console.WriteLine("ConcreteComponent.Operation()"); }}/// <summary>/// The 'ConcreteDecoratorA' class/// </summary>class ConcreteDecoratorA : Decorator{ public override void Operation() { base.Operation(); Console.WriteLine("ConcreteDecoratorA.Operation()"); }} /// <summary>/// The 'ConcreteDecoratorB' class/// </summary>class ConcreteDecoratorB : Decorator{ public override void Operation() { base.Operation(); AddedBehavior(); Console.WriteLine("ConcreteDecoratorB.Operation()"); } void AddedBehavior() { }}
PUTTING IT ALL TOGETHER Create handler
Query Command
Repository Repository actions
Create decorator Committing changes
USAGE THE TRANSACTION DECORATORpublic class TransactionRequestHandlerDecorator<...> : IConvertorCommandHandler<...>{ private readonly IConvertorCommandHandler<...> decorated; private readonly MyDatabaseContext context; public TransactionRequestHandlerDecorator(
IConvertorCommandHandler<...> decorated,MyDatabaseContext context)
{ this.decorated = decorated; this.context = context; }
public void Handle(TCommand command){ Log.Debug("Starting new transaction."); using (var transaction = context.Database.BeginTransaction()) { try { decorated.Handle(command); context.SaveChanges(); Log.Debug("Saving changes."); transaction.Commit(); Log.Debug("Comitting the transaction."); } ...
DECORATOR REGISTRATIONforeach (var commandHandler in commandHandlers){ builder.RegisterGeneric(commandHandler) .Named("commandHandler", typeof(IConvertorCommandHandler<,,>));}
builder.RegisterGenericDecorator( typeof(TransactionRequestHandlerDecorator<,,>), typeof(IConvertorCommandHandler<,,>), fromKey: "commandHandler")
.Named("decorated", typeof(IConvertorCommandHandler<,,>));
QUESTIONS?