views:

70

answers:

1

I am looking for the most appropiate way of dealing with a user activity feed on my social networking site. At the moment i have several activities which can appear on the news feed such as:

  • Users joins the site
  • User comments on a post
  • User adds a a post to their favourites
  • User adds a new post to the site

Here is a simplified version of my domain objects at the moment:

public abstract class NewsItem : Entity, ITenantSpecific  
{  

    public virtual Account Account { get; set; }  
    public virtual DateTime DateTime { get; set; }  

    // returns formatted news html string which gets 
    // overridden by inherted classes  
    public abstract string GetNewsHtml();  
}


public class NewsItemJoiner : NewsItem
{  
    public virtual Account AccountJoined { get; set; }

    public override string GetNewsHtml()
    {
        return "XXX has just joined our music network";
    }
}

As you can see at the moment I have a property which must be overridden on each activity called GetNewsHtml. This isn't ideal as I don't believe my domain should be responsible for generating my html.

I have thought about using a partial view for each activity type and pass into it the NewsItem base class downcasted into the correct type.

However I am open to suggestions.

A: 

I have a similar issue but with different order types. I decided to define rendering at the view layer (web/controllers), not domain. You can do it this way:

public interface IRenderer<T> where T: NewsItem 
{
   string Render(T item);
}

public class NewsItemJoinerRenderer: IRenderer<NewsItemJoiner>
{
   public string Render(T item)
   {
       return "XXX has just joined our music network";
   }
}

public class NewsRendererFactory
{
   public IRenderer<T> GetRenderer<T>()
   {
        return ServiceLocator.GetInstance<IRenderer<T>>();
   }
}

Then you can pass NewsRendererFactory to controller. Perhaps there's a way to avoid ServiceLocator but for now I cannot tell.

Notice that this makes your architecture both configurable and pluggable if needed.

You can define additional render-related interfaces, add more properties to the IRenderer - for example, PartialName, etc, or have lambda filters on IRenderer that Factory uses to decide if this interface implementation is applicable for the passed (to GetRenderer("some-condition")) condition. A lot of things are possible.

If you don't want IoC containers (ServiceLocator), you can have its job done with simple switch() statement inside NewsRendererFactory.GetRenderer. This will isolate the logic inside single factory method, and you'll be able to replace it easily with true IoC once you are ready.

Update: how to get renderers.

If you don't use IoC, you do something like

 typeof(IRenderer<>).Assembly.GetTypes().Where(x =>
        x.IsGenericType &&
        x.GetGenericTypeDefinition() == typeof(IRenderer<>) &&
        x.GetGenericArguments().FirstOrDefault() == requestedTypeArguments)

Then you can select SingleOrDefault() or ToList() if you can handle multiple renderers.

queen3
thanks for the answer. Sounds like exactly what i need to do and passes the responsibility of my display down to the controller level.Thanks!
Paul Hinett
I am trying to get this up and running, however I am struggling with working out how to call GetRenderer<T> when I have a list of different types of NewsItem's (such as a joiner, status update etc).Have you got a solution for this?
Paul Hinett
I've added answer on how to do this without IoC. IoC-related answer vary across different containers (Windsor, etc) so I can't give a generic one. For manual implementation, a simple caching would speed up the solution.
queen3
Hi, Forgive me if I am being dumb here but i'm fairly new to generics, with the piece of code you provded it seems to only return the IRenderer<T> interface instead of each of the concrete classes such as NewsJoinerRenderer : IRenderer<NewsItemJoiner>. I am also assuming that "requestedTypeArguments" should be the type of NewsItem that i want...such as typeof(NewsItemJoiner)?
Paul Hinett