Friday, March 30, 2012

Code Camp Presentation Tomorrow

So tomorrow I'll be giving a small presentation at New England Code Camp 17, entitled "What Is Dependency Injection?"

The presentation (materials for which can be found here) is very intro-level.  It's basically aimed at anybody who has experience writing business software but hasn't had a chance to take a look at dependency injection.  Maybe they've heard the term used, maybe they've seen someone implement it in code, etc.  Ultimately they've asked themselves, "Why does this look like it's more complex?  Why would I use it?"

The presentation walks through some basic definitions of dependency injection and reasons to use it.  The last few slides near the end are mostly notes and placeholders while switching over to Visual Studio and walking through the actual code.  The code evolves through four stages:
  • No Layers - This version of the code is a standalone application where the application code talks directly to the database.
  • Layers - This version of the code breaks out the data access into its own assembly and the application uses that assembly as a repository.
  • DI - This version of the code introduces an interface for the repository and introduces a dependency injection framework to wire up the implementation to that interface.
  • Domain - This version abstracts out the dependency injection framework itself as a dependency and inverts the project references so that the core domain code doesn't see (or care about) the dependency implementations at all.
The code isn't perfect, I'm sure.  Parts may be downright sloppy.  But that's fine.  The point is to quickly (within the span of a Code Camp session) walk through dependency injection as a concept and see how it can be implemented.  (The structure might look familiar to my former colleagues from BGC.  I felt that the basic structure used at the time worked pretty well for this demonstration.)

If you have any suggestions for improvements, please let me know.  I would also like to record this as a screencast at some point and post it here.  (The main hurdle to that is just finding a quiet place to record a few sessions.  Work doesn't offer much right now, and home has children.  I'll find time for something, don't worry.)

This is part of my ongoing efforts to get more speaking and writing experience in my career.  And, to that end, this will actually be my first public speaking engagement outside of the office.  Wish me luck!

Wednesday, March 28, 2012

In Case You Want to Write Something in Base13, or Base35, and so on

I got a little bored toward the end of the day at work today and randomly remembered back to a short exchange Tony and I had on Facebook a while back. It went something like this:
  • Tony: Good example of a Brain Study: If you can read this you have a strong mind:  7H15 M3554G3 53RV35 7O PR0V3 H0W 0UR M1ND5 C4N D0 4M4Z1NG 7H1NG5!  1MPR3551V3 7H1NG5!  1N 7H3 B3G1NN1NG 17 WA5 H4RD BU7 N0W, 0N 7H15 LIN3 Y0UR M1ND 1S R34D1NG 17 4U70M471C4LLY W17H 0U7 3V3N 7H1NK1NG 4B0U7 17, B3 PROUD!  0NLY C3R741N P30PL3 C4N R3AD 7H15.  PL3453 F0RW4RD 1F U C4N R34D 7H15 :)..
  • Me: 0100001001101001011101000110001101101000001011000010000001110000011011000110010101100001011100110110010100101110
  • Tony: 0101011101101000011000010111010001100101011101100110010101110010001011000010000001111001011011110111010100100000011101000110100001101001011011100110101100100000011110010110111101110101001001110111001001100101001000000111001101110000011001010110001101101001011000010110110000111111
  • Me: 93cba07454f06a4a960172bbd6e2a435
  • Tony: ERROR: Encoding not specified
  • Me:  What, you can't read MD5 hashes? Wuss.
At the time, I actually didn't want to use MD5.  For a moment I wanted to convert "Yes" to Base-13.  But I was pressed for time and a bit lazy and there was no convertor to be found to do that.

So now I wrote one. 

59908B8C2676267B90862684818C8C847A268C879435268C80768C308B2676848437

Monday, March 26, 2012

Catch != Handle

I'm getting really tired of this:

try
{
    // do something
}
catch
{
}

I'm sure we all are, really. Hopefully you don't encounter it nearly as often as I do. I would say that I should just lay off the Stack Overflow for a while to try to avoid this construct, but all too often I actually see this in production code at work.

(Needless to say that if I ever see it from someone on my team, rather than legacy code owned by a client, then someone's going to get chewed out military style. But I digress...)

However, this isn't that bad. The code is terrible, yes. But encountering it, even though it causes me to die a little inside, isn't really a bad thing. It's easy to explain to the owner of the code why it's bad. It's easy to correct the situation.

What is really starting to bother me is this:

try
{
    // do something
}
catch
{
    throw;
}

Or, worse, this:

try
{
    // do something
}
catch (Exception ex)
{
    throw ex;
}

Or, even worse, this:

try
{
    // do something
}
catch
{
    throw new Exception("Failed to do something");
}

I encounter this nonsense a lot, and the truly sinister part is that the owner vehemently defends this position. It's most commonly seen wrapping some database code, and the owner usually makes a point to try to wrap as little code as possible with this construct. Usually just the part which executes the database query. (Though I've even seen this construct repeated multiple times within a method. Once to surround opening the database, once to surround executing the query, etc.)

Whenever I try to point out the flaws in this approach, I'm met with variations of the same argument... "But you're supposed to wrap all database code in a try/catch! That's the right way to do it! Otherwise you're going to have exceptions happening!"

I'm sure at some impressionable point in the owner's career this concept was hammered into his head. Always catch database exceptions. And, in a way, he's right. But he interpreted it wrong. He's following the letter of the law with no regard for the spirit of the law. He's blindly applying a coarsely-defined policy at the expense of his code. (Something about which I've written before, and something against which I often argue.)

The key thing to note is the difference between "catching" an exception and "handling" an exception. They are not the same thing. Catching is a physical construct. It's part of the language. Handling, on the other hand, is a logic construct. It's part of the design of the code, independent of the language being used.

If the current context can handle the exception... that is, if it can meaningfully respond to it and logically continue the execution of the application... then that's where you catch it. If there's nothing meaningful to be done with the exception in the current context, then there's no reason to catch it.

The standard response to this notion from those who defend their position is that one should never let exceptions be unhandled. That would be madness, apparently. Exceptions being thrown? God help us. Seriously, though. This isn't a bad thing. Exceptions are not a bad thing. Don't be afraid of them.

Exceptions are not a bad thing. Don't be afraid of them.

Exceptions are our friends. They are how the framework reports errors. Use them. Especially use them instead of return codes. (Man do I hate return codes. They're a particular flavor of magic numbers/strings, and a distasteful flavor at that. It's one thing for a class or a method to have a magic string. It's another thing for that class or method to leak that magic string all over the place. Some designs call for return messages, such as standard response envelopes with Errors collections and what not. They're the exception, not the rule. No pun intended, honestly.)

The compiler even hints at the usefulness of exceptions in a very subtle way...

public User GetUser(string name)
{
    // TODO: implement this
}

The compiler doesn't like that. It tells us that this method has no code paths which return a value. This, however...

public User GetUser(string name)
{
    throw new NotImplementedException();
}

This is perfectly acceptable. Why? Because throwing an exception is a perfectly valid exit path for a method. It's a way to end the method and pop back up the stack.

Throwing an exception is a perfectly valid exit path for a method.

Again, it's all about context. If the method can meaningfully respond to the error in some way, then it should. But if it can't, then it shouldn't. It shouldn't even try. The method should exit in a state of error, pop back up the stack, and let something that can meaningfully respond to the exception do so.

Does this mean that the exception can go entirely unhandled? Absolutely. And that's a good thing. Well, it's not really a good thing. But it's good to call light to it as being a bad thing, rather than hide it away where bugs can fester. If the application ends up failing due to unhandled exceptions, that's a clear indication that, well, you need to go in there and handle them. If the application ends up with long-lived subtle bugs due to improperly handled exceptions, that's not a clear indication of anything and will cost more time and effort to determine the root cause and repair it.

So, in that sense, it's a good thing for a bad thing to happen. Assuming that a bug will exist, would you rather it make itself clearly known or hide away for years and corrupt more things along the way? This is another reason why exceptions are not to be feared. Don't suppress errors, embrace them. Errors tell you what's wrong and how to fix it.

Don't suppress errors, embrace them. Errors tell you what's wrong and how to fix it.

So what universal standard should one employ for error handling? I've heard arguments at both extremes. Some developers defend that exceptions should be caught as close to the event as possible. Others defend that exceptions should be caught as close to the UI as possible. They're both wrong. Or, at the very least, misguided in their approach. As I've said, exceptions should be caught where they can logically be handled.

For example...

Consider a web application. In true web application fashion, it handles requests and crafts responses. The structural life of an "instance" is that request/response scenario. Being RESTful (hopefully), there's no meaningful context outside of that scenario.

Say that a user submits a form to update a record in a database. The update fails. Maybe the database is down, maybe there's an error with the data, whatever. It's not important. What is important is that an exception was thrown. Where should that exception be handled?

Well, where is it most meaningful to handle the exception? The update failed, so the method which performs the update doesn't have anything more to do. It gives up. It doesn't try to recover, it doesn't try to make you feel warm and fuzzy. It has one responsibility... Update the data. When that fails, it gives up.

So the exception starts to bubble up the stack. This is good. It carries useful and meaningful context about the error with it. Now, where should it stop? In this case, it should probably stop very near to the UI. After all, contextually what actually failed? The request. So the response, which is a fairly immediate thing that the user needs to see, should contain an indication of the failure.

So, in this case, let the exception bubble up to the web application's global exception handler, or perhaps a unit-of-work exception handler, or some other request-wide handler which has the responsibility of handling errors in the request and reporting them back in the response. Show the user a meaningful error message, log details of what happened, and let the application continue about it's merry way. (In this case, continuing about its merry way means delivering the response and disposing of the current context, awaiting the next request.)

Now consider a different example. We can even suppose that this example is part of the overall system of which that web application was part. Let's say a user can upload a "file" to be "processed." In this case it's a CSV or otherwise record-based file, potentially large, where a back-end service will "process" each record (it doesn't matter what that means right now) and send some notification to the user when it's done.

So this back-end service has a fairly simple structure. It's probably a Windows Service or a console app or something that's just running unattended with no user interface. It checks a data store (database, directory on the file system, etc.) for new data to be processed and then processes it in a loop of some kind, record by record.

Now, let's imagine that one of the records can't be processed due to an error. Maybe some validation check failed, maybe there was a temporary hiccup in network connectivity to a necessary resource, whatever. Again, not important right now. The loop encountered a record that couldn't be processed. Where should the exception be handled?

Well, in the case of the developers who defend that it should happen as close to the UI as possible, we have a problem. What UI? The web application? No, that request is long since gone. The user has moved on to other things. The back-end service? It doesn't have a UI.

Should the service itself error out and let the system handle the error? (Basically end up in the Event Log, adrift in a sea of useless information?) No, definitely not. Just because one record couldn't be processed doesn't mean the whole service should give up. The method which processes that record should give up. But the method which processes that file has every reason to keep going.

So in this case the meaningful place to handle the exception is in the loop of the back-end service. Catch the exception, log it, in some way mark the record as unprocessed (a flag in the database, an edit to the file, some notification to the user such as an email or an in-app notification system in the database, etc.), and move on to the next record. That's where it logically makes sense to handle that particular error in that particular stack.

In either of these scenarios, we could have caught the exception anywhere. As a physical construct, that can happen anywhere the language supports it. But there were more specific places where it made sense to handle the exception. As a logical construct, there are relatively few places where it truly makes sense.

Catching an exception is easy. Handling it requires one to actually think about the context of the given scenario. And thinking is a good thing.

Friday, March 23, 2012

Change Tracking with an Event

So I've continued to tinker with this Collection As A Repository thing today, off and on between other work. Something about it just keeps me interested, and it's a welcome break from the non-coding work I have to do on my current project.

I seem to be in a good spot right now in terms of tracking changes. What I've done is expand my domain model's properties to be more manual and to raise an event when they change (INotifyPropertyChanged, of course, since I imagine that will be useful in other contexts as well). Then the collection listens on that event for any model it returns and, in response to the event, updates the local data context.

So at this point here's the custom model:

public class Product : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;

  private int _productID;
  public int ProductID
  {
    get
    {
      return _productID;
    }
  }

  private string _name;
  public string Name
  {
    get { return _name; }
    set
    {
      _name = value;
      if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs("Name"));
    }
  }

  private string _productNumber;
  public string ProductNumber
  {
    get { return _productNumber; }
    set
    {
      _productNumber = value;
      if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs("ProductNumber"));
    }
  }

  private decimal _listPrice;
  public decimal ListPrice
  {
    get { return _listPrice; }
    set
    {
      _listPrice = value;
      if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs("ListPrice"));
    }
  }

  private Product() { }

  public Product(string name, string productNumber, decimal listPrice)
  {
    _name = name;
    _productNumber = productNumber;
    _listPrice = listPrice;
  }

  public Product(int productID, string name, string productNumber, decimal listPrice)
  {
    _productID = productID;
    _name = name;
    _productNumber = productNumber;
    _listPrice = listPrice;
  }
}

It took me a while to come to terms with the fact that I'm not using auto-implemented properties anymore. I just love the simple clean syntax of them. And the repetition of these properties... Not so much. But the convincing argument I keep giving myself is that these are not merely data structures. These are potentially rich domain models. It's fully acceptable and expected for their properties to be expanded out and to contain stuff under the hood.

And here's the current state of my "repository":

public interface IProducts : IEnumerable<Product>
{
  Product this[int id] { get; }
  Product this[string name] { get; }
  void Add(Product product);
  void Remove(Product product);
  void SaveChanges();
}

And the implementation that I'm injecting for it:

public class Products : IProducts
{
  private AdventureWorksDataContext _db = new AdventureWorksDataContext();
  private IList<Product> _products = new List<Product>();

  public Models.Product this[int id]
  {
    get
    {
      if (id < 1)
        throw new ArgumentException("ID must be a positive integer.");

      if (ObjectHasNotBeenFetchedFromTheDatabase(id: id))
        FetchObjectFromTheDatabase(id: id);
      return TransformEntityToModel(_products.Single(p => p.ProductID == id));
    }
  }

  public Models.Product this[string name]
  {
    get
    {
      if (string.IsNullOrWhiteSpace(name))
        throw new ArgumentException("Name must be a valid string.");

      if (ObjectHasNotBeenFetchedFromTheDatabase(name: name))
        FetchObjectFromTheDatabase(name: name);
      return TransformEntityToModel(_products.Single(p => p.Name == name));
    }
  }

  public void Add(Models.Product product)
  {
    var productEntity = TransformModelToEntity(product);
    _products.Add(productEntity);
    _db.Products.InsertOnSubmit(productEntity);
  }

  public void Remove(Models.Product product)
  {
    var productEntity = TransformModelToEntity(product);
    _products.Remove(TransformModelToEntity(product));
    _db.Products.DeleteOnSubmit(productEntity);
  }

  public void SaveChanges()
  {
    _db.SubmitChanges();
    _products.Clear();
  }

  public System.Collections.IEnumerator GetEnumerator()
  {
    var localProducts = _products.Select(p => p.ProductID);
    foreach (var product in _products.Concat(_db.Products.Where(p => !localProducts.Contains(p.ProductID))).OrderBy(p => p.ProductID))
      yield return TransformEntityToModel(product);
  }

  IEnumerator<Models.Product> IEnumerable<Models.Product>.GetEnumerator()
  {
    var localProducts = _products.Select(p => p.ProductID);
    foreach (var product in _products.Concat(_db.Products.Where(p => !localProducts.Contains(p.ProductID))).OrderBy(p => p.ProductID))
      yield return TransformEntityToModel(product);
  }

  private bool ObjectHasNotBeenFetchedFromTheDatabase(int id = 0, string name = "")
  {
    if ((id == 0) && (string.IsNullOrWhiteSpace(name)))
      throw new ArgumentException("Must supply either an id or a name.");

    if (id > 0)
      return _products.SingleOrDefault(p => p.ProductID == id) == null;
    else
      return _products.SingleOrDefault(p => p.Name == name) == null;
  }

  private void FetchObjectFromTheDatabase(int id = 0, string name = "", Product product = null)
  {
    if ((id == 0) && (string.IsNullOrWhiteSpace(name)) && (product == null))
      throw new ArgumentException("Must supply either an id or a name or a product.");

    if (id > 0)
      _products.Add(_db.Products.Single(p => p.ProductID == id));
    else if (!string.IsNullOrWhiteSpace(name))
      _products.Add(_db.Products.Single(p => p.Name == name));
    else
      _products.Add(product);
  }

  private Models.Product TransformEntityToModel(Product product)
  {
    var productModel = new Models.Product(product.ProductID, product.Name, product.ProductNumber, product.ListPrice);
    productModel.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(Changed);
    return productModel;
  }

  private Product TransformModelToEntity(Models.Product product)
  {
    var existingProduct = _products.SingleOrDefault(p => p.ProductID == product.ProductID);
    if (existingProduct == null)
      existingProduct = _db.Products.SingleOrDefault(p => p.ProductID == product.ProductID);
    return existingProduct ?? new Product
    {
      ProductID = product.ProductID,
      Name = product.Name,
      ProductNumber = product.ProductNumber,
      ListPrice = product.ListPrice,
      SafetyStockLevel = 1000,
      ReorderPoint = 750,
      SellStartDate = DateTime.Now,
      ModifiedDate = DateTime.Now
    };
  }

  private void Changed(object product, PropertyChangedEventArgs args)
  {
    var eventProduct = product as Models.Product;
    var existingProduct = _products.SingleOrDefault(p => p.ProductID == eventProduct.ProductID);
    if (existingProduct == null)
    {
      FetchObjectFromTheDatabase(id: eventProduct.ProductID);
      existingProduct = _products.Single(p => p.ProductID == eventProduct.ProductID);
    }
    existingProduct.ProductID = eventProduct.ProductID;
    existingProduct.Name = eventProduct.Name;
    existingProduct.ProductNumber = eventProduct.ProductNumber;
    existingProduct.ListPrice = eventProduct.ListPrice;
  }
}

It's very rough at the moment, of course. Much of the code can be re-factored and cleaned up. There are probably edge cases I haven't discovered yet. (Some of the code in there now is to handle the edge case of adding an object to the collection and then removing it before committing it, for example.) The general idea of having that local copy of stuff can certainly use some polishing. On the one hand, it provides a potentially useful caching construct. On the other hand, it's kind of ugly and makes other code throughout the class kind of ugly.

At the moment I'm not making it production-worthy. I'm just making a proof of concept. And one of the concepts I think I'd like to try with this is getting rid of SaveChanges() entirely and just using an implied save any time anything changes. Of course, if I go that route then I'll definitely want to implement a Unit Of Work object to wrap it all up (unless I just want to assume the use of MSDTC).

Thursday, March 22, 2012

Collection as a Repository

Yesterday I posted about how I'm finding myself conceptually moving away from generic repositories and into more custom ones. And I started to think about the idea of using a custom collection as a repository.

Today I threw together a simple little proof of concept just to get a picture of what it is I'm actually thinking. Currently it's sort of just a pass-through to Linq2Sql tables, using the Linq2Sql objects (for their change tracking capabilities). This is what I threw together:

public interface IProducts : IEnumerable
{
  Product this[int id] { get; set; }
  Product this[string name] { get; set; }
  void Add(Product product);
  void Remove(Product product);
  void SaveChanges();
}

public class Products : IProducts
{
  private AdventureWorksDataContext _db = new AdventureWorksDataContext();

  public Product this[int id]
  {
    get
    {
      return _db.Products.Single(p => p.ProductID == id);
    }
    set
    {
      var existingProduct = _db.Products.Single(p => p.ProductID == id);
      existingProduct.Name = value.Name;
      existingProduct.ProductNumber = value.ProductNumber;
      existingProduct.ListPrice = value.ListPrice;
    }
  }

  public Product this[string name]
  {
    get
    {
      return _db.Products.Single(p => p.Name == name);
    }
    set
    {
      var existingProduct = _db.Products.Single(p => p.Name == name);
      existingProduct.Name = value.Name;
      existingProduct.ProductNumber = value.ProductNumber;
      existingProduct.ListPrice = value.ListPrice;
    }
  }

  public void Add(Product product)
  {
    _db.Products.InsertOnSubmit(product);
  }

  public void Remove(Product product)
  {
    _db.Products.DeleteOnSubmit(product);
  }

  public void SaveChanges()
  {
    _db.SubmitChanges();
  }

  public System.Collections.IEnumerator GetEnumerator()
  {
    foreach (var product in _db.Products)
      yield return product;
  }
}

I really like using indexers like that. Think of them like .GetBySomeKey() methods, but a little cleaner in their usage. After all, I always find .GetBySomeKey() methods to feel clunky. We're just trying to fetch an object from a collection based on a key, right? Well the language already has a standard for doing just that. Think of a Dictionary object, for example.

I'd also like to experiment a little with the different interfaces to implement. IList will be more involved, but may have a more intuitive and cleaner result.

Naturally, there's some re-factoring to do in the code above. The setters on the indexers can certainly be re-factored and cleaned up. (And, indeed, I kept them at a minimum just to demonstrate the concept... There are considerably more fields than the three being set.)

My biggest problem with this so far is that I'm using the Linq2Sql objects as my models, which means my DAL implementation is bleeding out into my domain. I find that distasteful and don't want such things polluting my model. However, without it I run into a problem of change tracking. For example, if I use a simple object as my domain model:

public class Product
{
  public int ProductID { get; set; }
  public string Name { get; set; }
  public string ProductNumber { get; set; }
  public decimal ListPrice { get; set; }
}

(Imagine this with more business logic baked into it, of course... But I'm keeping it simple for this post.)

Now I need to translate between my database entities and my business models. So adding one would look more like this:

public void Add(Models.Product product)
{
  _db.Products.InsertOnSubmit(new Product
  {
    ListPrice = product.ListPrice,
    Name = product.Name,
    ProductID = product.ProductID,
    ProductNumber = product.ProductNumber
  });
}

That's all well and good, but what about when I get an item:

public Product this[int id]
{
  get
  {
    return _db.Products
              .Select(p => new Models.Product
              {
                ListPrice = p.ListPrice,
                Name = p.Name,
                ProductID = p.ProductID,
                ProductNumber = p.ProductNumber
              })
              .Single(p => p.ProductID == id);
  }
  set
  {
    // ...
  }
}

Oops, now I've lost my change tracking. When using this, I would intuitively expect .SaveChanges() to save the changes I make to the models I fetch from the collection. But in this example it won't. So, of course, I need to bake more intelligence into this. The question becomes whether I can keep that change tracking intelligence from overly polluting my model. (Normally when I use repositories it's all pretty well abstracted into the DAL and the models don't know or care about it, but that's because the repositories have all kinds of explicit GetBySomething() and Save() methods where I can intuitively expect that logic to reside. This is a more abstracted interface, so this will require more thought.)

Tuesday, March 20, 2012

I May Be Done With Generic Repositories

I love repositories. I really do. It's one of the first patterns I learned anything about, it's my most used pattern, etc. And the first thing I've always done is to define the interface for the repository. And, invariably, I always come to some new realization about the design of the repository each time I do it.

For example, I once used an interface very similar to this:

public interface IRepository<T>
{
  IEnumerable<T> GetAll();
  T GetByID(int id);
  void Save(T model);
  void Delete(T model);
  void DeleteByID(int id);
}

That's all well and good, and it works. But after a while it didn't sit right with me. I didn't like the GetAll() and GetByID(). So the next time around I tried this:

public interface IRepository<T>
{
  IEnumerable<T> Get();
  void Save(T model);
  void Delete(T model);
  void DeleteByID(int id);
}

At this point I rely on chaining queries onto Get() to actually get stuff. So if I want to get by the ID, I'd do something like this:

SomeRepositoryInstance.Get().Single(foo => foo.ID == id);

Again, it works. This freed me from having to generically define my gets, since different models have different keys on which to commonly get.

Then I started to dislike the Delete() and its ilk. So I'd add some kind of "mark to delete" mechanism on the models themselves and just include it in the Save():

public interface IRepository<T>
{
  IEnumerable<T> Get();
  void Save(T model);
}

It's starting to get a little light. And now, in more recent iterations with more complex domains, I may be done with this generic interface entirely.

A few realities go against this interface and others along the same pattern:
  • Not every model has a repository. So I'd have to jump through hoops to try to limit what can have repositories. This can result in bare repository implementations with NotImplementedExceptions being thrown, which is ugly. Or in complex IoC bootstrapping. Or all kinds of little workarounds.
  • Not every model can be inserted, or updated, or deleted. Some just aren't supposed to be as a result of business logic. And that's a domain logic concern, not a data access layer concern. So how would one reconcile that logic in the implementations of the generic repository?
  • Some repositories need extra stuff. So I have an IRepository for all models, and some of them also have an IModelRepository? That made for unintuitive usage by other developers. (Recognizing the fact that myself a few months later counts as an "other developer.")

These realities got me thinking about the entire concept of the generic repository. It's great for things like straight Linq-to-SQL where every database entity is also a model (don't get me started on that). But for more complex domains where either you're defining the models first before the persistence (something most companies still don't like to do for some reason) or where the two are growing/evolving a lot over time, it doesn't seem to work very well.

So I'm at the point now where I'm basically just dropping the IRepository and sticking with custom repositories. IFooRepository, IBazRepository, etc. A repository shouldn't be some second-class citizen that gets handed over to some code generation tool and generically applied across the board. There's business logic to be applied here. Like I said, what if some of the models are un-deleteable. Or, worse, only deleteable in certain context. That business logic belongs in the domain where those repository interfaces are defined.

Interestingly enough, this same leap has led me to another design consideration. The repository itself isn't anything special, structurally. It represents a very simple concept... a list of models from a data store. So, from the perspective of the domain, the repository implementation is simply a collection of models.

An IList, if you will.

This presents an opportunity for object oriented thinking. What logic should be encapsulated within the model, and what logic should be encapsulated within the collection of the model? A colleague and I just discussed this today and it had never really occurred to me, but he's right. Some logic belongs on the collection, and not all collections are as generic as I used to want my repositories to be.

For example, what if for a particular model there's a business rule that says there can never be more than five of them together? That shouldn't be a concern of the model itself, but it is a concern of a collection of the model. So if my "repositories" implement IEnumerable and maintain their own iterators, then they can internally contain the logic of collections of the models.

I'll have to try this out and see how it works, but I'm intrigued by the idea.