Why MS-CRM SOAP interfaces suck
I woke up this morning to an email from Matt Powell asking me to clarify why the platform interfaces suck. Well, "suck" wasn't really the word he used, it was more of an echo from Simon Fell about the interfaces because they "blow chunks". Nice way to start the morning, but not unusual. Great timing though because I had a 9am meeting about those very interfaces and what we're going to do about them.
I can't go into exact detail because this stuff is still something we're just batting around, but we are looking at a very different flavor. The idea is to trim the endpoints down dramatically, make them very chunky (in a different way though Simon), bake in some WS-* stuff, and generally make the programming model more programmer friendly.
Well, back to the topic at hand. The V1.x platform interfaces are kinda ugly to work with. No, let's be honest, they do suck to work with. So, as a client I need to build up an XML string without any help from the tools, make sure it's really XML and not just a string, slam it at a platform interface that's got a weird name, returns more string stuff, and takes other not useful parameters (CUserAuth is something that just doesn't make any sense any more, it's a left-over from a very early design that we couldn't take the time to rip out without breaking everything... trust me, I wish it had gone away)... anyway, the APIs suck.
But, if we look at the tools that were available at the time we started designing and implementing V1 it becomes pretty clear that we really didn't have a choice. The .Net framework was in early betas without a clear ship date, the CLR hadn't been used for anything in the business applications space yet, and we were looking at hosted scenarios in bCentral (read: this was supposed to be fast and at the time the framework didn't look that way). So we did some looking around and found a project called Manta over in the ATL world. Well, Manta went on to become ATL Server and sure looked like it would work for us.
[Oh yeah, and it was really simple for our soon-to-be-built application framework to build up those magic XML strings right in the browser, POST them to the application server, and have them forwarded untouched to the platform. Talk about few transforms... there were none, the XML was POSTed directly as an XML document, the application server reached in to the document and pulled out what it needed, if anything, and the document was handed to the platform as a string. Pretty much the same thing on the way from the platform to the application and on to the browser - the platform handed the string to the application layer which ran an XSLT over it, and HTML went out to the browser.]
The only problem was that we had a hard requirement that our entity definitions had to support change once deployed. That is, our customers typically customize CRM solutions to meet their business needs. That's what CRM is all about. Well, ATL Server only supported C++/COM types. So, the problem became how do we provide a SOAP interface ('cause damn it we were going to support SOAP and web services even if it killed us) that wasn't too hard to program against, was cross-platform friendly, supported WSDL, and supported extensible types. We chose the XML-as-strings path for V1.x. To make some of the pain go away we provided a client-side proxy so developers wouldn't need to spend a lot of cycles trying to make the WSDL-based client-side classes behave properly (can you say namespace collisions?).
For V1.2 we spent a lot of time looking at ways to wrap the platform API set in something friendlier but the closest we came (because of the extensibility issues) was to use an "object" called a property bag. Well, that sucks more than XML strings, or at least that's my feeling. That, combined with the fast turnaround time, and the need to not break any deployed applications, meant that we weren't able to do anything better in that version.
I've always hated the fact that our interface names seemed to match the entity against which they operated, sometimes. It gets really confusing when someone asks why they can't create a CRMAccount class in C#, set up some properties, then call a method on it. That's a general service-oriented disconnect between the SOA patterns and the old school OO patterns. One thing we're doing in early work is to start naming the interfaces FooService for example and the parameter is usually a Foo. But that leads to other problems; the same question comes up about mixing things but with the names cleaned up the confusion gets easier to clear up.
On to V2 (don't ask me about timelines 'cause I won't say anything). We are seriously looking at introducing a cleaner model across the entire platform. We're doing this for a few reasons. The first is that we're introducing a managed platform for a number of V2 entities and that means that we'd have an ATL Server-based interface and a managed ASMX interface with two different programming models. We even considered butchering the V2 interfaces so they look and behave like 1.x interfaces. Ick.
The current plan is that we're trashing both interfaces in favor of something very lightweight, very VS-friendly, and closer to an SOA model. The idea is to chop the API set down to:
and finally...
The parameters are really the driving force. The service is secondary. The client sets up the "command" they want executed by constructing a class, filling in some basic information, and submitting it to the service. Most of the methods can take their type information directly from the supplied parameter. For Execute the BusinessDocument describes the method, the parameters, and other interesting bits.
Oh yeah, and those 6 methods are the entire API set. We'll see what happens. There's a long way to go defining this stuff and we still need to get some decent prototype work in place to make sure we can support the new model and have some back-compat for the old model for a while yet.
I can't go into exact detail because this stuff is still something we're just batting around, but we are looking at a very different flavor. The idea is to trim the endpoints down dramatically, make them very chunky (in a different way though Simon), bake in some WS-* stuff, and generally make the programming model more programmer friendly.
Well, back to the topic at hand. The V1.x platform interfaces are kinda ugly to work with. No, let's be honest, they do suck to work with. So, as a client I need to build up an XML string without any help from the tools, make sure it's really XML and not just a string, slam it at a platform interface that's got a weird name, returns more string stuff, and takes other not useful parameters (CUserAuth is something that just doesn't make any sense any more, it's a left-over from a very early design that we couldn't take the time to rip out without breaking everything... trust me, I wish it had gone away)... anyway, the APIs suck.
But, if we look at the tools that were available at the time we started designing and implementing V1 it becomes pretty clear that we really didn't have a choice. The .Net framework was in early betas without a clear ship date, the CLR hadn't been used for anything in the business applications space yet, and we were looking at hosted scenarios in bCentral (read: this was supposed to be fast and at the time the framework didn't look that way). So we did some looking around and found a project called Manta over in the ATL world. Well, Manta went on to become ATL Server and sure looked like it would work for us.
[Oh yeah, and it was really simple for our soon-to-be-built application framework to build up those magic XML strings right in the browser, POST them to the application server, and have them forwarded untouched to the platform. Talk about few transforms... there were none, the XML was POSTed directly as an XML document, the application server reached in to the document and pulled out what it needed, if anything, and the document was handed to the platform as a string. Pretty much the same thing on the way from the platform to the application and on to the browser - the platform handed the string to the application layer which ran an XSLT over it, and HTML went out to the browser.]
The only problem was that we had a hard requirement that our entity definitions had to support change once deployed. That is, our customers typically customize CRM solutions to meet their business needs. That's what CRM is all about. Well, ATL Server only supported C++/COM types. So, the problem became how do we provide a SOAP interface ('cause damn it we were going to support SOAP and web services even if it killed us) that wasn't too hard to program against, was cross-platform friendly, supported WSDL, and supported extensible types. We chose the XML-as-strings path for V1.x. To make some of the pain go away we provided a client-side proxy so developers wouldn't need to spend a lot of cycles trying to make the WSDL-based client-side classes behave properly (can you say namespace collisions?).
For V1.2 we spent a lot of time looking at ways to wrap the platform API set in something friendlier but the closest we came (because of the extensibility issues) was to use an "object" called a property bag. Well, that sucks more than XML strings, or at least that's my feeling. That, combined with the fast turnaround time, and the need to not break any deployed applications, meant that we weren't able to do anything better in that version.
I've always hated the fact that our interface names seemed to match the entity against which they operated, sometimes. It gets really confusing when someone asks why they can't create a CRMAccount class in C#, set up some properties, then call a method on it. That's a general service-oriented disconnect between the SOA patterns and the old school OO patterns. One thing we're doing in early work is to start naming the interfaces FooService for example and the parameter is usually a Foo. But that leads to other problems; the same question comes up about mixing things but with the names cleaned up the confusion gets easier to clear up.
On to V2 (don't ask me about timelines 'cause I won't say anything). We are seriously looking at introducing a cleaner model across the entire platform. We're doing this for a few reasons. The first is that we're introducing a managed platform for a number of V2 entities and that means that we'd have an ATL Server-based interface and a managed ASMX interface with two different programming models. We even considered butchering the V2 interfaces so they look and behave like 1.x interfaces. Ick.
The current plan is that we're trashing both interfaces in favor of something very lightweight, very VS-friendly, and closer to an SOA model. The idea is to chop the API set down to:
BusinessEntity Create(Type) -- create a new, initialized, but not persisted entity
Delete(BusinessEntity) -- we're debating whether delete is exposed or if delete is really just a state change. There are strong feelings on both sides.
EntityCollection Find(Criteria) -- using the criteria get a collection of entities
BusinessEntity Get(EntityMoniker) -- retrieve exactly one (or zero) entity
SaveOutcome Save(BusinessEntity) -- put the entity in the database or update an existing one
and finally...
Execute(BusinessDocument, Criteria) -- using Criteria, find a collection of entities and apply the business methods specified in BusinessDocument. There's a lot of cool stuff in this approach that allows us to support replaceable method implementations, workflow-driven implementations, and ISV-defined functionality. This would heavily leverage the current metadata but would greatly extend it.
The parameters are really the driving force. The service is secondary. The client sets up the "command" they want executed by constructing a class, filling in some basic information, and submitting it to the service. Most of the methods can take their type information directly from the supplied parameter. For Execute the BusinessDocument describes the method, the parameters, and other interesting bits.
Oh yeah, and those 6 methods are the entire API set. We'll see what happens. There's a long way to go defining this stuff and we still need to get some decent prototype work in place to make sure we can support the new model and have some back-compat for the old model for a while yet.