<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7912437711674480886</id><updated>2012-02-16T00:39:58.461-08:00</updated><category term='Design'/><category term='ASP.Net MVC'/><category term='REST'/><category term='Programming'/><title type='text'>Random Metaphors</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://randommetaphors.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7912437711674480886/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://randommetaphors.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Random Metaphors</name><uri>http://www.blogger.com/profile/07967990618588731235</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>9</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7912437711674480886.post-3453017431688587598</id><published>2009-10-27T20:17:00.000-07:00</published><updated>2009-10-28T03:39:08.328-07:00</updated><title type='text'>notes on access control</title><content type='html'>access control is usually an application concern -- think current user.&lt;br /&gt;&lt;br /&gt;if access control is done in the domain, the it should be part of the domain language, not calls to security check.&lt;br /&gt;e.g. &lt;br /&gt;Role.Approves(request) &lt;- the security is built into the language here&lt;br /&gt;Boss.EntersExecutiveBathroom(urgency) &lt;- again, built into the language.&lt;br /&gt;&lt;br /&gt;Thinking about using permissions immediately pushes into application concern and does not belong in the domain.&lt;br /&gt;if (user.hasPermision(permission)) &lt;- this sort of stuff&lt;br /&gt;&lt;br /&gt;see http://tech.groups.yahoo.com/group/domaindrivendesign/message/15484&lt;br /&gt;&lt;br /&gt;Application rules set a sort context for the domain to work in. Moving domain to a different application might lose some rules because they only made sense/were defined in that one application/context.&lt;br /&gt;&lt;br /&gt;In the domain, do not talk about "the current user". Talk explicitly of the roles and what the roles can do.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7912437711674480886-3453017431688587598?l=randommetaphors.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://randommetaphors.blogspot.com/feeds/3453017431688587598/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7912437711674480886&amp;postID=3453017431688587598' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7912437711674480886/posts/default/3453017431688587598'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7912437711674480886/posts/default/3453017431688587598'/><link rel='alternate' type='text/html' href='http://randommetaphors.blogspot.com/2009/10/notes-on-access-control.html' title='notes on access control'/><author><name>Random Metaphors</name><uri>http://www.blogger.com/profile/07967990618588731235</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7912437711674480886.post-5599863583191417230</id><published>2009-10-24T08:30:00.000-07:00</published><updated>2009-10-24T16:22:38.415-07:00</updated><title type='text'>Thoughs on how to refactor an ugly ActiveRecord into something more flexible.</title><content type='html'>Separate command and query in your domain layer.&lt;br /&gt;&lt;br /&gt;Command Accepting entities should not have public state.&lt;br /&gt;&lt;br /&gt;Command Accepting entities probably have a bunch of overloads to Accept() with the parameter being the type.&lt;br /&gt;&lt;br /&gt;Might be a good idea to have a domain event aggregator. This is like an internal message bus. There are several good articles out there about how to build one. Udi has one that I've used.&lt;br /&gt;&lt;br /&gt;So, when a Command-Entity accepts a command it should publish internal state changes as events to the event aggregator. You can then take care of different concerns in different event handlers. Concerns might be:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Bring the eventually consistent reporting and/or query stores up to date.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The event logger should capture this too if you are using event streaming. Somehow this has to be fail-proof. Not sure how to ensure this yet. If it fails, issue compensating command? Logger has first chance to process and if fails, don't allow events to be publish?&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Probably a good idea to make commands idempotent. If you are using a distributed system timestamps on the commands aren't very good. Guid probably can only help if you keep a blacklist of already process commands. This sort of has risks in multi-threaded scenarios. I saw Ayende comment about this. Another good technique is to ensure commands are handled serially and compare a version number.&lt;br /&gt;&lt;br /&gt;Still haven't found any good examples of how to deal with large event stream stores. The DBA on a project is recommending Oracle indexed table with advanced compression using Oracle Xml DB. Maybe using a GUID-comb for the entity id.&lt;br /&gt;&lt;br /&gt;A command accept method might look like:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt; Acquire lock. Unless you are sure this command wont step on the currently executing one.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;ValidationEngine.Validate(command, entityDTO)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I guess ValidationEngine would be a domain service injected into the Entity. Some folks say this is bad at all costs. Some say domain services are ok just don't inject infrastructure services. I'd love to see some real world examples of how to determine which is which. In that case the command handler is external to the entity. This makes it infrastructure. So then you can inject query services to get the DTO and use a ValidationService in combination with command and DTO data.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;PerformComputation(command)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;PublishStateChanges() and other events if necessary.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Release lock if necessary&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;The command distributor would be responsible for logging the command. Also ensuring security concerns. Guess security is an infrastructure concern?&lt;br /&gt;&lt;br /&gt;Not all business rules are handled inside the domain. Things like ensuring uniqueness of a given key is handled the simplest way closest to set itself. If using a SQL database, use a unique key constraint on the on the reporting/query store. If this fails, issue a compensating command. This sounds fine and all about what about the usable state of the entity in between that time? What about other listeners of the event? How do they get compensation? I suppose entity version in commands would prevent any old query derived commands from executing but what if the query store succeeds but the event logs fails? That could allow version OK commands, that really should get processed but timing could allow it. Can the event log talk to the query store? Maybe answered above.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7912437711674480886-5599863583191417230?l=randommetaphors.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://randommetaphors.blogspot.com/feeds/5599863583191417230/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7912437711674480886&amp;postID=5599863583191417230' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7912437711674480886/posts/default/5599863583191417230'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7912437711674480886/posts/default/5599863583191417230'/><link rel='alternate' type='text/html' href='http://randommetaphors.blogspot.com/2009/10/thoughs-on-how-to-refactor-ugly.html' title='Thoughs on how to refactor an ugly ActiveRecord into something more flexible.'/><author><name>Random Metaphors</name><uri>http://www.blogger.com/profile/07967990618588731235</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7912437711674480886.post-3798219347028596886</id><published>2009-09-18T05:45:00.000-07:00</published><updated>2009-09-18T05:47:03.651-07:00</updated><title type='text'>Projects list</title><content type='html'>Automapper&lt;br /&gt;blueprint-css&lt;br /&gt;Fluent Validator&lt;br /&gt;Json.NET&lt;br /&gt;Linfu&lt;br /&gt;Migrator.NET&lt;br /&gt;NBehave&lt;br /&gt;NServiceBus_2&lt;br /&gt;Rhino Mocks 3.6&lt;br /&gt;RikMigrations&lt;br /&gt;Run-Sharp&lt;br /&gt;StructureMap 2.5.4&lt;br /&gt;Topshelf&lt;br /&gt;xVal&lt;br /&gt;Machine for mSpec and Migrations&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7912437711674480886-3798219347028596886?l=randommetaphors.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://randommetaphors.blogspot.com/feeds/3798219347028596886/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7912437711674480886&amp;postID=3798219347028596886' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7912437711674480886/posts/default/3798219347028596886'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7912437711674480886/posts/default/3798219347028596886'/><link rel='alternate' type='text/html' href='http://randommetaphors.blogspot.com/2009/09/projects-list.html' title='Projects list'/><author><name>Random Metaphors</name><uri>http://www.blogger.com/profile/07967990618588731235</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7912437711674480886.post-6864532578194886228</id><published>2009-08-09T14:45:00.000-07:00</published><updated>2009-08-09T14:48:48.362-07:00</updated><title type='text'>again, turn it inside out, SRP</title><content type='html'>So you don't want infrastructure leaking into your domain model.&lt;br /&gt;Make another class that encapsulates your domain with a reference and then&lt;br /&gt;has all the infrastructure details. The repository will have a list of these&lt;br /&gt;wrappers instead of just a list of the domain models.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7912437711674480886-6864532578194886228?l=randommetaphors.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://randommetaphors.blogspot.com/feeds/6864532578194886228/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7912437711674480886&amp;postID=6864532578194886228' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7912437711674480886/posts/default/6864532578194886228'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7912437711674480886/posts/default/6864532578194886228'/><link rel='alternate' type='text/html' href='http://randommetaphors.blogspot.com/2009/08/again-turn-it-inside-out-srp.html' title='again, turn it inside out, SRP'/><author><name>Random Metaphors</name><uri>http://www.blogger.com/profile/07967990618588731235</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7912437711674480886.post-5653608965467867325</id><published>2009-07-08T15:10:00.000-07:00</published><updated>2009-07-08T15:46:18.127-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='REST'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.Net MVC'/><title type='text'>Ambiguous Action handlers in ASP.Net MVC</title><content type='html'>The RESTful URLs present a little bit of a challenge to the ASP.Net MVC framework. The example I had is where I want to post various different forms to the same URL. What I would like to do is take advantage of the automatic model mapping that having a form in the argument list for action method. At this point, out of the box you will get an ambiguous action error message since you have two method with the same name, just different argument lists. The reason for the same name is the Simply RESTful routing handler uses a standard set of possible actions. In the post case, you must have an action named Create.&lt;br /&gt;&lt;br /&gt;There is something you can do about this. In the answer to&lt;a href="http://stackoverflow.com/questions/1045316/asp-net-mvc-ambiguous-action-methods"&gt; MVC Ambigous action methods&lt;/a&gt; an action attribute is coded and derived from ActionMethodSelectorAttribute. This has a method IsValidForRequest which seems to be called by the framework when figuring which action method to invoke. So I modified this a bit, knowing that all my forms will have different prefixed, if a Param key has that prefix I know use that method.&lt;br /&gt;&lt;p&gt;&lt;br /&gt;so in a controller I have&lt;/p&gt;&lt;br /&gt;&lt;pre style="overflow: scroll"&gt;&lt;br /&gt;  [RequireFormPrefix("z")]&lt;br /&gt;  public ActionResult Shmoo(Model1 z)&lt;br /&gt;  {&lt;br /&gt;      return View("Shmoo1", z);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  [RequireFormPrefix("q")]&lt;br /&gt;  public ActionResult Shmoo(Model2 q)&lt;br /&gt;  {&lt;br /&gt;&lt;br /&gt;      return View("Shmoo2", q);&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and&lt;br /&gt;&lt;pre style="overflow: scroll"&gt;&lt;br /&gt;public class RequireFormPrefixAttribute : ActionMethodSelectorAttribute&lt;br /&gt;{&lt;br /&gt;  public RequireFormPrefixAttribute(string formPrefix)&lt;br /&gt;  {&lt;br /&gt;      FormPrefix = formPrefix;&lt;br /&gt;  }&lt;br /&gt;  public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)&lt;br /&gt;  {&lt;br /&gt;      // holy moly this is a law of demeter violation&lt;br /&gt;      foreach(var key in controllerContext.HttpContext.Request.Params.Keys)&lt;br /&gt;      {&lt;br /&gt;          if (key.ToString().StartsWith(FormPrefix + "."))&lt;br /&gt;          return true;&lt;br /&gt;      }&lt;br /&gt;      return false;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public string FormPrefix { get; private set; }&lt;br /&gt;}&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7912437711674480886-5653608965467867325?l=randommetaphors.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://randommetaphors.blogspot.com/feeds/5653608965467867325/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7912437711674480886&amp;postID=5653608965467867325' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7912437711674480886/posts/default/5653608965467867325'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7912437711674480886/posts/default/5653608965467867325'/><link rel='alternate' type='text/html' href='http://randommetaphors.blogspot.com/2009/07/ambiguous-action-handlers-in-aspnet-mvc.html' title='Ambiguous Action handlers in ASP.Net MVC'/><author><name>Random Metaphors</name><uri>http://www.blogger.com/profile/07967990618588731235</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7912437711674480886.post-5340075978336064440</id><published>2009-06-21T14:35:00.000-07:00</published><updated>2009-07-08T15:26:50.477-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Design'/><title type='text'>Easier to find or remember</title><content type='html'>Object Oriented principles and practices&lt;br /&gt;&lt;ul&gt;&lt;li&gt;SOLID&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Single Responsibility Principle&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Open/Closed Principle&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Liskov Substitution Principle&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Interface Segregation&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Dependency Inversion&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Law of Demeter&lt;/li&gt;&lt;br /&gt;&lt;li&gt;DRY - don't repeat yourself&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;YAGNI - you ain't gonna need it&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7912437711674480886-5340075978336064440?l=randommetaphors.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://randommetaphors.blogspot.com/feeds/5340075978336064440/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7912437711674480886&amp;postID=5340075978336064440' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7912437711674480886/posts/default/5340075978336064440'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7912437711674480886/posts/default/5340075978336064440'/><link rel='alternate' type='text/html' href='http://randommetaphors.blogspot.com/2009/06/easier-to-find-or-remember.html' title='Easier to find or remember'/><author><name>Random Metaphors</name><uri>http://www.blogger.com/profile/07967990618588731235</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7912437711674480886.post-8072210403831614569</id><published>2009-06-21T14:25:00.000-07:00</published><updated>2009-06-21T14:51:51.185-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Design'/><title type='text'>Reminder notes on some object types/</title><content type='html'>Just something I want to remember without having to do a web search.&lt;br /&gt;&lt;br /&gt;DTO = data transfer object&lt;br /&gt;VO = value object or transfer object.&lt;br /&gt;&lt;br /&gt;Two main differences between DTOs and VOs.&lt;br /&gt;First is mutability. DTOs are mutable property bags and VOs are immutable.&lt;br /&gt;&lt;br /&gt;The second difference between DTOs and VOs is how equality is determined. DTOs are based on object identity and value objects are based on state. I think this means to say that if you override the Equals() on a DTO that computes on state data you have successfully converted a DTO into a VO. Another rephrase is that you must override the Equals() on a VO.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Got this from http://anirudhvyas.com/root/2008/04/19/abuses-of-dto-pattern-in-java-world/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7912437711674480886-8072210403831614569?l=randommetaphors.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://randommetaphors.blogspot.com/feeds/8072210403831614569/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7912437711674480886&amp;postID=8072210403831614569' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7912437711674480886/posts/default/8072210403831614569'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7912437711674480886/posts/default/8072210403831614569'/><link rel='alternate' type='text/html' href='http://randommetaphors.blogspot.com/2009/06/dto-data-transfer-object-vo-value.html' title='Reminder notes on some object types/'/><author><name>Random Metaphors</name><uri>http://www.blogger.com/profile/07967990618588731235</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7912437711674480886.post-4166369390002946102</id><published>2008-08-05T14:43:00.000-07:00</published><updated>2008-08-05T15:49:56.207-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='REST'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.Net MVC'/><title type='text'>RESTful Searching</title><content type='html'>Digging into the wild world of REST where there seems to be a lot of information and conventions, I also find there is a lot lacking. I did notice on another blog, wish I kept the reference, that there are a lot of things REST experts would agree on but really no repository of "better practices".&lt;br /&gt;&lt;br /&gt;The rails conventions make it very easy to publish entities and do repository style filtering of all the entities of a known type. This is typically done by a GET to a resource URI and most likely with query parameters. This URI GET is usually mapped to a controller's index method. I have some interesting question regarding caching that I think are satisfied using the VARY Http header. Consider that the authorization cookie is actually part of the filter criteria. Meaning results could vary depending on who is doing the searching. Alas, I digress.&lt;br /&gt;&lt;br /&gt;My biggest problem so far is what happens if there are user modifiable properties of each search result. Say, there is a check box on each row for a multiple choice scenario. We can't use a GET to modify a resource as its against the idempotency&lt;em&gt; &lt;/em&gt;law. Well, we could put this property on the backing entity. Then we have to figure out what to do if multiple people are searching and toggling this selection. Clicking this box would then POST to the backing entity URI. Thing is, how would the controller for the resource POST know to redirect back to the exact search URI? I'm sure people really don't want to tack on a redirect property. I don't particularly like this one.&lt;br /&gt;&lt;br /&gt;Perhaps, the result in the set of results is its own entity. Efficiency nerds would cringe at keeping copies of data in multiple forms, especially for multiple users and then perhaps even multiple times for the same user. Ok, so we don't expose each result per se but rather, allow a POST to the search URI where it can maintain state of all the selected results (one way would be to have a form with all the chosen ids in a hidden inputs along with a choose link which submits them. But to where?) I've seen that when others have stumbled into the problem where a search combines heterogeneous entities, they seem to go for a separate search URI as well.&lt;br /&gt;&lt;br /&gt;Anyhow, I think I will experiment with this approach and see how it goes.&lt;br /&gt;Expose the resource URI as&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;/ResourceCollection &lt;/li&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;POST[Create new]&lt;/li&gt;&lt;li&gt;GET[non-filterable list, page-able? OK]&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;li&gt;/ResourceCollection/ID&lt;/li&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;GET[get it]&lt;/li&gt;&lt;li&gt;POST[not defined]&lt;/li&gt;&lt;li&gt;PUT[Update]&lt;/li&gt;&lt;li&gt;DELETE[kill it]&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;li&gt;/ResourceCollection/New&lt;/li&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;GET[returns a form that represents the entity and will POST to /ResourceCollection]&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;li&gt;/ResourceCollection/ID/EDIT&lt;/li&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;GET[return form for editing that PUTs to /ResourceCollection/ID]&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;li&gt;/ResourceCollection/ID/DELETE&lt;/li&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;GET[return form for confirming delete, it DELETE to /ResourceCollection/ID]&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Then for search have the following scheme&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;/ResourceSearch&lt;/li&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;POST[create new search results, redirects to /ResourceSearch/ID]&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;li&gt;/ResourceSearch/New&lt;/li&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;GET[form to create new search results]&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;li&gt;/ResourceSearch/ID This resource should/will be security context sensitive. Only the user who created it should be able to work with it.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;GET[typically with query parameters for paging and sorting].&lt;br /&gt;&lt;/li&gt;&lt;li&gt;POST[updates something about search results, accept post entity of only a search result or perhaps a property of a search result]&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;/ul&gt;This seems like a bit more work but perhaps more flexible. Is it all still RESTful? I think so. Now, what to do with all these sets of search results?!&lt;br /&gt;&lt;br /&gt;Then again, maybe there is a better metaphor to solve this problem than sticking a checkbox on a result row. Create a selected resource which is just a maintained list of result rows? Your search results view would then possibly show this list along with the paged results from search? The list of search results would have buttons to add to the selected list?&lt;br /&gt;&lt;br /&gt;I'm also coming at this from non-Ajax perspective. In an Ajax world, the client could maintain the list of selected items.&lt;br /&gt;&lt;br /&gt;The experiment will be implemented in the ASP.Net MVC framework and using the provided SimplyRestful routing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7912437711674480886-4166369390002946102?l=randommetaphors.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://randommetaphors.blogspot.com/feeds/4166369390002946102/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7912437711674480886&amp;postID=4166369390002946102' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7912437711674480886/posts/default/4166369390002946102'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7912437711674480886/posts/default/4166369390002946102'/><link rel='alternate' type='text/html' href='http://randommetaphors.blogspot.com/2008/08/restful-searching.html' title='RESTful Searching'/><author><name>Random Metaphors</name><uri>http://www.blogger.com/profile/07967990618588731235</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7912437711674480886.post-5192251881848489720</id><published>2008-01-02T10:19:00.000-08:00</published><updated>2008-01-02T10:27:44.059-08:00</updated><title type='text'>First Post</title><content type='html'>Welcome, this is just a test post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7912437711674480886-5192251881848489720?l=randommetaphors.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://randommetaphors.blogspot.com/feeds/5192251881848489720/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7912437711674480886&amp;postID=5192251881848489720' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7912437711674480886/posts/default/5192251881848489720'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7912437711674480886/posts/default/5192251881848489720'/><link rel='alternate' type='text/html' href='http://randommetaphors.blogspot.com/2008/01/first-post.html' title='First Post'/><author><name>Random Metaphors</name><uri>http://www.blogger.com/profile/07967990618588731235</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
