Microsoft's prescribed Enterprise SOA solution at the time: CSF, WCF and BizTalk • We had an Enterprise / Technical Architect round-table • Split Internal systems into different independent SOA services (we really wanted to do SOA :) • Enterprise architects defined web service contracts using UML and XSDs: • XSD Messages were implementation ignorant • Explicitly validated XSD conformance at runtime • Leveraged SOAP/XML/XSD: Headers, Inheritance, Namespaces • UML -> XSD's -> DTOs -> used in WCF Service Contracts -> WSDLs -> Service Reference Proxies • CSF's state-full session was used as a Bus orchestrating communication between services • Services were discoverable, participating in CSF's Session • Service endpoints were WCF Services, leveraged SOAP headers for extra WS-* compliance • Wrote code Designed perfectly normalized databases Followed the .NET Enterprise Play Book
existing messages • Infectious: Domain logic binded to code-gen DTOs and version changes led to multiple parallel implementations • Friction-encumbered: Services needed to be built/ deployed in lock-step with each other Disaster Code-gen DTO's were: • Xml everywhere: We had XML config to manage our XML- configured services and resources • Week-long downtimes: between breaking major upgrades, required to flush old messages out of the system • Glacial Development: The turn-around time to get a field from UML/XSD/dll to C# was measured in days • Enterprise Hacks: Added "ViewBags" to most messages to allow us to stuff arbitrary data in existing messages without changing the schema We pioneered:
time-decoupled, re-usable messages • Pub/Sub: Promotes the most loosely coupled architecture i.e. Sender decoupled from Receiver • Message Queues: Replay-able async idempotent messages provide the most reliable and durable inter-service communications • Martin Fowler & Co knew what the best-practices for remote services were: • Remote Facade: Using message-based, coarse-grained/batch-full interfaces minimizing round-trips, promoting creation of fewer but tolerant and version-able service interfaces • DTOs: Avoid any effort expended dealing with wire-format in application code • Gateway: Removes comms logic in app code and provides a swappable comms layer with a good isolation and testing abstraction
force the use of a particular technology or platform It's NOT to conform to a pre-defined specification or constraints It's NOT to use a pre-determined format or follow a pre-scribed architecture
encapsulate some re-usable capabilities and make them available remotely ★ To as many clients as possible ★ In the most accessible and interoperable way ★ With the least amount of effort ★ With maximum amount of re-use Good characteristics ★ Version-able, tolerant and resilient to changes ★ End-to-end productivity ★ Goals that ServiceStack is optimized for :) Legend:
evolve as you're freely able to add/remove functionality without error • They're easy to route, chain, hijack and decorate through to different handlers and pipelines • They're easy to serialize and proxy through to remote services • They're easy to log, record, defer and replay • They're easy to map and translate to and from domain models • Ideal for concurrency as immutable messages are thread-safe and are easily multiplexed • They're easy to extend with generic solutions and re-usable functionality around https://github.com/ServiceStack/ServiceStack/wiki/Advantages-of-message-based-web-services
string | Fetch of IChannel<Map<string,int>> | Stop type WordCountingAgent() = let counter = MailboxProcessor.Start(fun inbox -> let rec loop(words : Map<string,int>) = async { let! msg = inbox.Receive() match msg with | Word word -> if words.ContainsKey word then let count = words.[word] let words = words.Remove word return! loop(words.Add (word, (count + 1)) ) else return! loop(words.Add (word, 1) ) | Stop -> return () | Fetch replyChannel -> do replyChannel.Post(words) //post to reply channel and continue return! loop(words) } loop(Map.empty)) //The initial state member a.AddWord(n) = counter.Post(Word(n)) member a.Stop() = counter.Post(Stop) member a.Fetch() = counter.PostSync(fun replyChannel -> Fetch(replyChannel)) http://strangelights.com/blog/archive/2007/10/24/1601.aspx Concurrency in F# – Part III – Erlang Style Message Passing
scala.actors.Actor._ import java.net.{InetAddress, UnknownHostException} case class LookupIP(name: String, respondTo: Actor) case class LookupResult( name: String, address: Option[InetAddress] ) object NameResolver extends Actor { def act() { loop { react { case LookupIP(name, actor) => actor ! LookupResult(name, getIp(name)) } } } } type ReadReq struct { key string ack chan<- string } type WriteReq struct { key, val string } c := make(chan interface{}) go func() { m := make(map[string]string) for { switch r := (<-c).(type) { case ReadReq: r.ack <- m[r.key] case WriteReq: m[r.key] = r.val } } }() Actors in Go
{ Customer GetCustomerById(int id); Customer[] GetCustomerByIds(int[] id); Customer GetCustomerByUserName(string userName); Customer[] GetCustomerByUserNames(string[] userNames); Customer GetCustomerByEmail(string email); Customer[] GetCustomerByEmails(string[] emails); } public class Customers { int[] Ids; string[] UserNames; string[] Emails; } public class CustomersResponse { Customer[] Results; } Any combination of the above can be fulfilled by 1 remote call, by the same single web service - i.e what ServiceStack encourages Fewer and more batch-full services require less maintenance and promote the development of more re-usable and efficient services. In addition, message APIs are much more resilient to changes as you're able to safely add more functionality or return more data without breaking or needing to re-gen existing clients. Message-based APIs also lend them better for cached, asynchronous, deferred, proxied and reliable execution with the use of brokers and proxies.
XmlServiceClient(BaseUri), new JsvServiceClient(BaseUri), new MsgPackServiceClient(BaseUri), } ; protected static IServiceClient[] ServiceClients = RestClients.OfType<IServiceClient>().ToArray(); [Test, TestCaseSource("RestClients")] public void Can_GET_AllReqstars(IRestClient client) { var allReqstars = client.Get<List<Reqstar>>("/reqstars"); Assert.That(allReqstars.Count, Is.EqualTo(ReqstarsService.SeedData.Length)); } [Test, TestCaseSource("ServiceClients")] public void Can_SEND_AllReqstars_PrettyTypedApi(IServiceClient client) { var allReqstars = client.Send(new AllReqstars()); Assert.That(allReqstars.Count, Is.EqualTo(ReqstarsService.SeedData.Length)); } New API Tests https://github.com/ServiceStack/ServiceStack/blob/master/tests/RazorRockstars.Console.Files/ReqStarsService.cs Same Unit Test can be re-used in JSON, XML, JSV, SOAP 1.1/1.2 Integration Tests https://github.com/ServiceStack/ServiceStack/blob/master/tests/ServiceStack.WebHost.IntegrationTests/Tests/WebServicesTests.cs
Name { get; set; } } [Route("/hellohtml/{Name}")] public class HelloHtml { public string Name { get; set; } } [Route("/helloimage/{Name}")] public class HelloImage { public string Name { get; set; } public int? Width { get; set; } public int? Height { get; set; } public int? FontSize { get; set; } public string Foreground { get; set; } public string Background { get; set; } } [Route("/hello/{Name}")] public class Hello : IReturn<HelloResponse> { public string Name { get; set; } } public class HelloResponse { public string Result { get; set; } } public class HelloService : Service { public object Get(HelloHtml request) { return "<h1>Hello, {0}!</h1>".Fmt(request.Name); } [AddHeader(ContentType = "text/plain")] public object Get(HelloText request) { return "<h1>Hello, {0}!</h1>".Fmt(request.Name); } [AddHeader(ContentType = "image/png")] public object Get(HelloImage request) { var width = request.Width.GetValueOrDefault(640); var height = request.Height.GetValueOrDefault(360); var bgColor = request.Background != null ? Color.FromName(request.Background) : Color.ForestGreen; var fgColor = request.Foreground != null ? Color.FromName(request.Foreground) : Color.White; var image = new Bitmap(width, height); using (var g = Graphics.FromImage(image)) { g.Clear(bgColor); var drawString = "Hello, {0}!".Fmt(request.Name); var drawFont = new Font("Times", request.FontSize.GetValueOrDefault(40)); var drawBrush = new SolidBrush(fgColor); var drawRect = new RectangleF(0, 0, width, height); var drawFormat = new StringFormat { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Center }; g.DrawString(drawString, drawFont, drawBrush, drawRect, drawFormat); var ms = new MemoryStream(); image.Save(ms, ImageFormat.Png); return ms; } } public object Get(Hello request) { return new HelloResponse { Result = "Hello, {0}!".Fmt(request.Name) }; } } C# Client calls with: //var response = client.Get(new Hello { Name = "ServiceStack" });
RAM 80GB HDD 4TB traffic Where to host servicestack.net? 1.67 GHz 1.75GB RAM 100GB HDD 2TB traffic €219.50 mo €2,634 year €19.90 mo €238.8 year > 11x Cheaper!
AppSettings(); Plugins.Add(new AuthFeature( () => new CustomUserSession(), //Use your own typed Custom UserSession type new IAuthProvider[] { new CredentialsAuthProvider(), //HTML Form post of UserName/Password credentials new TwitterAuthProvider(appSettings), //Sign-‐in with Twitter new FacebookAuthProvider(appSettings), //Sign-‐in with Facebook new DigestAuthProvider(appSettings), //Sign-‐in with Digest Auth new BasicAuthProvider(), //Sign-‐in with Basic Auth new GoogleOpenIdOAuthProvider(appSettings), //Sign-‐in with Google OpenId new YahooOpenIdOAuthProvider(appSettings), //Sign-‐in with Yahoo OpenId new OpenIdOAuthProvider(appSettings), //Sign-‐in with Custom OpenId })); https://github.com/ServiceStack/SocialBootstrapApi/ https://github.com/ServiceStack/ServiceStack/wiki/Authentication-and-authorization
: Service { public object Get(CachedOrders request) { var cacheKey = "unique_key_for_this_request"; return base.RequestContext.ToOptimizedResultUsingCache(base.Cache, cacheKey, () => { //Delegate is executed if item doesn't exist in cache //Any response DTO returned here will be cached automatically } ); } } Redis, Memcached, Disk and Azure Caching Providers also available Sessions works with any Cache Provider