The Greek Fire Experiment

The Greek Fire Experiment is simple.

Architect a solution this is

  • simple enough for any developer to follow and implement
  • abstract enough to handle technology change
  • testable

This is a simple exercise Ryan Riley and I are engaging in. We jot down ideas, put in them into code… and then hit it with the pragmatist hammer of reality!

I am going to start posting progress and share ideas we have had and tried.

The solutions won’t be complete nor perfect. The point is to show our progress and get feedback.

I am going to start off simple. I will then evolve the solution and the domain as progress is made.

This is a learning process. I want someone without the knowledge of the patterns and technology to come to grasp with why things were done a certain way.

There is a gap between those you understand the vernacular and concepts and the everyday coder.

The discussion and concepts are express at the 10,000 foot level without any code. So everyone nods but then goes back to their cubicle without any clue on how to implement towards the ideals.

We are working on the bridge to span that gap. Bridging the gap from everyday code to the latest technology, latest techniques, and latest patterns.

And that! Is the point of the Greek Fire Experiment.

Let’s start…

Currently the domain consist of an Expense Application.

We have an Expense consisting of an id, an amount, a title, and a description.

   1: /// <summary>
   2:     /// Expense Entity.
   3:     /// An expense has a title, description, and an amount
   4:     /// </summary>
   5:     public class Expense
   6:     {
   7:         #region Properties
   8:  
   9:         /// <summary>
  10:         /// Gets or sets the id.
  11:         /// </summary>
  12:         /// <value>The id.</value>
  13:         public int Id { get; set; }
  14:  
  15:         /// <summary>
  16:         /// Gets or sets the amount for the expense.
  17:         /// </summary>
  18:         /// <value>The amount.</value>
  19:         public decimal Amount { get; set; }
  20:  
  21:         /// <summary>
  22:         /// Gets or sets the description.
  23:         /// </summary>
  24:         /// <value>The description.</value>
  25:         public string Description { get; set; }
  26:  
  27:         /// <summary>
  28:         /// Gets or sets the title.
  29:         /// </summary>
  30:         /// <value>The title.</value>
  31:         public string Title { get; set; }
  32:  
  33:         #endregion
  34:     }

I am using the DataContext interface Fredrik Kalseth provided in his blog post.

The DataContext will act as a Unit of Work (a pattern described by Martin Fowler).

   1: /// <summary>
   2:     /// Interface for the DataContext which acts as a Unit of Work
   3:     /// Abstracts inserts into the database and provides repositories for Entities
   4:     /// </summary>
   5:     public interface IDataContext
   6:     {
   7:         /// <summary>
   8:         /// Gets the current change set for the unit of work.
   9:         /// </summary>
  10:         /// <value>The change set.</value>
  11:         IChangeSet ChangeSet { get; }
  12:  
  13:         /// <summary>
  14:         /// Repositories for entities
  15:         /// </summary>
  16:         /// <typeparam name="T"></typeparam>
  17:         /// <returns></returns>
  18:         IQueryable<T> Repository<T>() where T : class;
  19:  
  20:         /// <summary>
  21:         /// Adds the item to the list of items to be inserted upon commit
  22:         /// </summary>
  23:         /// <typeparam name="T"></typeparam>
  24:         /// <param name="item">The item.</param>
  25:         void Insert<T>(T item) where T : class;
  26:  
  27:         /// <summary>
  28:         /// Commits the current unit of work to the database.
  29:         /// </summary>
  30:         void Commit();
  31:     }

For the DataContext, currently I only have an in memory data context. This allows me to build tests as I grow the solution.

   1: /// <summary>
   2: /// Memory Data Context implements IDataContext and uses
   3: /// an in memory data store
   4: /// </summary>
   5: public class MemoryDataContext : IDataContext
   6: {
   7:     #region Properties
   8:  
   9:     private List<object> MemoryDataStore { get; set; }
  10:  
  11:     public IChangeSet ChangeSet { get; protected set; }
  12:  
  13:     #endregion
  14:  
  15:     #region Constructor
  16:  
  17:     /// <summary>
  18:     /// Initializes a new instance of the <see cref="MemoryDataContext"/> class.
  19:     /// </summary>
  20:     public MemoryDataContext()
  21:     {
  22:         ChangeSet = new ChangeSet();
  23:         MemoryDataStore = new List<object>();
  24:     }
  25:  
  26:     #endregion
  27:  
  28:     #region Methods
  29:  
  30:     /// <summary>
  31:     /// Repositories for entities
  32:     /// </summary>
  33:     /// <typeparam name="T"></typeparam>
  34:     /// <returns></returns>
  35:     public IQueryable<T> Repository<T>() where T : class
  36:     {
  37:         IEnumerable<object> query = from objects in MemoryDataStore
  38:                                     where typeof (T).IsAssignableFrom(objects.GetType())
  39:                                     select objects;
  40:  
  41:  
  42:         return query.Select(o => (T) o).AsQueryable();
  43:     }
  44:  
  45:     /// <summary>
  46:     /// Adds the item to the list of items to be inserted upon commit
  47:     /// </summary>
  48:     /// <typeparam name="T"></typeparam>
  49:     /// <param name="item">The item.</param>
  50:     public void Insert<T>(T item) where T : class
  51:     {
  52:         ChangeSet.AddInsert(item);
  53:         MemoryDataStore.Add(item);
  54:     }
  55:  
  56:     /// <summary>
  57:     /// Commits the current unit of work to the database.
  58:     /// </summary>
  59:     public void Commit()
  60:     {
  61:         ChangeSet.Reset();
  62:         RaiseCompleted(EventArgs.Empty);
  63:     }
  64:  
  65:     #endregion
  66:  
  67:     #region Completed Event 
  68:  
  69:     public event EventHandler Completed;
  70:  
  71:     /// <summary>
  72:     /// Raises the completed event.
  73:     /// </summary>
  74:     /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
  75:     private void RaiseCompleted(EventArgs e)
  76:     {
  77:         EventHandler completedHandler = Completed;
  78:  
  79:         if (completedHandler != null)
  80:             completedHandler(this, e);
  81:     }
  82:  
  83:     #endregion
  84: }

The Expense Model is a Business Model which allows GetById and to Save an Expense. And it’s so simple it only allows inserts.

   1: /// <summary>
   2: /// Expense Model is the Business Model for Expense.
   3: /// GetsById and Saves Expense into the DataContext.
   4: /// </summary>
   5: public class ExpenseModel
   6: {
   7:     #region Properties
   8:  
   9:     private IDataContext DataContext { get; set; }
  10:  
  11:     #endregion
  12:  
  13:     #region Constructors
  14:  
  15:     /// <summary>
  16:     /// Initializes a new instance of the <see cref="ExpenseModel"/> class
  17:     /// using the passed in DataContext as a Unit of Work
  18:     /// </summary>
  19:     /// <param name="dataContext">The data context.</param>
  20:     public ExpenseModel(IDataContext dataContext)
  21:     {
  22:         DataContext = dataContext;
  23:     }
  24:  
  25:     #endregion
  26:  
  27:     #region Methods
  28:  
  29:     /// <summary>
  30:     /// Gets the expense by id.
  31:     /// </summary>
  32:     /// <param name="id">The id.</param>
  33:     /// <returns></returns>
  34:     public Expense GetById(int id)
  35:     {
  36:         return DataContext.Repository<Expense>()
  37:             .Where(e => e.Id == id)
  38:             .FirstOrDefault();
  39:     }
  40:  
  41:     /// <summary>
  42:     /// Saves the expense either inserting or updating the datacontext
  43:     /// </summary>
  44:     /// <param name="expense">The expense.</param>
  45:     public void Save(Expense expense)
  46:     {
  47:         var foundExpense = GetById(expense.Id);
  48:  
  49:         if (foundExpense == null)
  50:         {
  51:             DataContext.Insert(expense);
  52:         }            
  53:     }
  54:  
  55:     #endregion
  56: }