An opinionated abstraction of DataFactory?

Given that you use EPiServer CMS and Page Type Builder and are interested in testing (or rather writing maintainable and flexible applications that abide by well tested design principles) you have probably found yourself needing an abstraction/facade/wrapper for EPiServer’s DataFactory that doesn’t just abstract a few common methods, such as IPageSource, or every single method in DataFactory such as EPiAbstractions IDataFactoryFacade but instead aid you in your daily work by abstracting, and implementing, commonly used operations utilizing the type safety and polymorphism that using Page Type Builder provides.

What I’m proposing is creating an interface and standard implementation of it that:

  • Has a GetPage() method.
  • Has a GetPage<T>() that returns an instance of the specified page that is of type T, given that the page is of a page type that matches or inherits from T. Otherwise it returns null.
  • Has a GetChildren() that works just as DataFactory’s.
  • Has a GetChildren<T>() that returns children of type T.
  • Has a GetDefaultPageData<T>(). Figuring out the Id of a page type when creating a page programmatically is at best boring…
  • Has a GetDescendents() method that works just as DataFactory’s.

Optionally it could also:

  • Have a GetAll<T>() that returns all pages of the exact type T, implemented using FindPagesWithCriteria.
  • Have a GetDescendents<T>() implemented using recursive calls to GetChildren.
  • Have a GetChildrenVisibleForVisitor<T>() that returns children after first filtering them using FilterForVisitor’s Filter method. Optionally it could accept the user as a parameter and use a different way to filter than the FilterForVisitor class.
  • Have a GetChildrenVisibleInMenus<T>() that works like the above but also filters out pages not visible in menus.

Questions

If I go through with this there are a few questions that needs answering:

  1. What should the interface and class be called? IPageRepository and PageRepository?
  2. Where should it have it’s home? It’s dependent on Page Type Builder but doesn’t belong it Page Type Builder. I’m thinking about adding a new project to EPiAbstractions called EPiAbstractions.Opinionated because that’s just what this is, an opinionated abstraction.
  3. What methods should it include? Have I missed any in the above listings?
  4. Are the non-typed methods (GetPage, GetChildren) even necessary? You can just as well use GetPage<PageData> as GetPage().
  5. Should methods that returns collections return IEnumerable<T> or List<T> or something else? Why?
  6. Would you use it or would you (continue) creating your own wrappers for DataFactory?

What do you think? Let me know with a comment!

PS. For updates about new posts, sites I find useful and the occasional rant you can follow me on Twitter. You are also most welcome to subscribe to the RSS-feed.

Comments

  1. Magnus Paulsson's avatar

    Magnus Paulsson 1 years ago

    Not an answer to your questions, but a general comment/opinion: I don't think GetChildrenVisibleForVisitor or GetChildrenVisibleInMenus should be included in the interface. I would rather have general VisibleForVisitor and VisibleInMenus extension methods. It just feels cleaner to leave the filtering out of the interface, otherwise why not have GetChildrenAlphabetical, GetChildrenByIndex, GetDescendantsStartingWithLetterB (ok that's absurd, but I'm trying to make a point here ;) ). I might be missing something though, that requires the methods to be in there.

  2. Joel Abrahamsson's avatar

    Joel Abrahamsson 1 years ago

    Magnus,

    The problem with extension methods is that you cannot isolate your code from the code in them. That's not a problem for simple methods that do string manipulation, filtering based on properties of objects in a collection etc. But when an extension method has external dependencies any code that uses it is more or less impossible to test using unit tests.

    Therefore methods for filtering must be declared in something abstract, such as an interface.

    One could argue though that there should be a separate interface for filtering. I agree with that, but from a pragmatic standpoint, given that it's extremely common to retrieve a page's children filtered by access it might be warranted to include that in the general interface? Would love to hear more reactions to this view though!

  3. Magnus Paulsson's avatar

    Magnus Paulsson 1 years ago

    Hah, you could say that I completely missed the point of it all then :)

  4. Stefan Forsberg's avatar

    Stefan Forsberg 1 years ago

    Some additions to the MSN-feedback I already gave =)

    1. Yes, sounds reasonable

    5. IEnumerable, ICollection or IList. As long as it's not List or another of the concrete implementations.

    Another thing, I'm not sure exactly what data it should return but have you given any thoughts to return some form of Null Object from instead of null? To save the user of a lot if null checks (instead of try/catch).

  5. Joel Abrahamsson's avatar

    Joel Abrahamsson 1 years ago

    Stefan,

    Could you elaborate regarding #5? It would be convenient to get Lists, but on the other hand I figure that the methods will then have to do .ToList() while in 80-90% of the cases you can just do with IEnumerable.

    Regarding null objects etc I got some feedback regarding that on Twitter. My current idea is to let the methods continue to throw PageNotFoundException as well as a new PageNotOfExpectedTypeException but also add a couple of TryGetPage methods. What do you think?

  6. Stefan Forsberg's avatar

    Stefan Forsberg 1 years ago

    I'd say go with IEnumerable then. Btw, on another sort of related subject. How will your FilterForVisitor methods work? Will they convert the IEnumerables to PageDataCollections and use EPi's filters and then back to IEnumerable or are you doing the filtering yourself?

    Aha, a TryGet approach would work really well I think.

  7. Joel Abrahamsson's avatar

    Joel Abrahamsson 1 years ago

    I'm thinking that they will look something like this:

    PageDataCollection children = DataFactory.Instance.GetPage();
    PageDataCollection filteredChildren = FilterForVisitor.Filter(children);
    return filteredChildren.OfType();
    
  8. Blake Payne's avatar

    Blake Payne 1 months ago

    Happy New Year to All!

Follow me on Twitter

  1. @chraas You might want to use it for complex parts of the site and not use it for simple rendering pages. 2 days ago
  2. @chraas In theory more SOLID, but I'm not sure it's worth the price. 2 days ago
  3. @chraas Be warned that you're introducing one big chunk of complexity with EPiMVP :) 2 days ago
follow me

Latest comments

  1. Berra S wrote "Read your post at http://joelabrahamsson.com/entry/using-xfo..." on PageData objects not returned as typed when using Page Type Builder and FindPagesWithCriteria
  2. Linus wrote "1 up for behaviour being as close as expected as possible!" on A common problem with Page Type Builder and UniqueValuePerLanguage set to false
  3. Joel Abrahamsson wrote "Hi Hans, Could it be that you previously didn't have Page..." on Page Type Builder 2.0 released

About this site

This blog is built with EPiServer Community, EPiServer CMS, ASP.NET MVC and a bunch of other great products. The source code is available for download at the projects page, where you also can read more about this site and my other projects.

read more