<?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:blogger='http://schemas.google.com/blogger/2008' 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-2889634717122112415</id><updated>2025-10-29T07:27:00.399-07:00</updated><category term="Sonatribe BDD CQRS"/><category term="Sonatribe ddd CQRS BDD"/><category term="consul"/><category term="ddd/cqrs"/><category term="kubernetes"/><category term="minikube"/><category term="pulumi"/><title type='text'>iWayneo</title><subtitle type='html'>Personal blog of Wayne Douglas. I am a Brighton based developer. My predominant skillset is around the .NET space but I am quickly learning Go! and Erlang as I am getting slight lang fatigue. &#xa;&#xa;Main interests are around architecture (CQRS/ES), languages, DevOps and multi OS environments</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>wayne</name><uri>http://www.blogger.com/profile/00603417641518780876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>55</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-7681814443196546691</id><published>2021-05-21T04:15:00.009-07:00</published><updated>2021-05-24T06:49:50.121-07:00</updated><title type='text'>The Architechture behind highstreet.ly</title><content type='html'>&lt;p&gt;&amp;nbsp;I recently read a blog post (&lt;a href=&quot;https://anthonynsimon.com/blog/one-man-saas-architecture/&quot;&gt;https://anthonynsimon.com/blog/one-man-saas-architecture/&lt;/a&gt;) by a guy called Anthony Simon, discussing the architecture behind his one man startup. I found it encouraging to see someone elses setup and how it&#39;s been architected.&lt;/p&gt;&lt;p&gt;I&#39;ve worked for a lot of larger (and therefor big budget) clients who utilise cloud, on-prem or hybrid aproaches. Some of these clients spend&amp;nbsp;£10,000 a week JUST on logging infrastructure! Clearly that kind of budget is way out of reach for a startup, and so when you&#39;re a self funded startup you need to look at ways of running things the best you know how- at budget! And so this is why Anthny&#39;s post struck a cord with me. We all know how to build highly scalable systems with budget, but when it&#39;s just&amp;nbsp; you, trying to figure it all out without the huge infrastructure, budget, teams etc - it&#39;s a little isolating!&amp;nbsp;&lt;/p&gt;&lt;p&gt;And so, because I found Anthony&#39;s post so useful, I thought I should share what we have @ highstreet.ly.&lt;/p&gt;&lt;p&gt;First up a little background.&amp;nbsp;&lt;/p&gt;&lt;p&gt;Our small team is comprised of 4:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Myself - I do all of the infrastructure, hosting, backend coding and 50% of the front end&lt;/li&gt;&lt;li&gt;Chris - Front end&lt;/li&gt;&lt;li&gt;Michael - marketing and business&amp;nbsp;&lt;/li&gt;&lt;li&gt;Jon - Design&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;We currently run it from our homes - 2 of us in the south of England, 1 in the Midlands and 1 in Mexico!&lt;/p&gt;&lt;p&gt;We started off as a ticketing platform, and before that a native app for festivals. But we never really got too far with that and things changed last year...&amp;nbsp;&lt;/p&gt;&lt;p&gt;Highstreet.ly was a pandemic baby. We were aware of the struggle that lots of highstreet shops were managing pre-covid - but during covid things clearly got a lot worse. We knew that alot of bricks and mortar businesses were struggling to compete with other services offering either similar products with fast delivery and competetive pricing or utilising the highstreet businesses and wrapping them in a last-mile delivery solution and charging them HUGE fees for the pleasure. We wanted to give these businesses an option to compete where they aren&#39;t being cheated - we wanted to help empower the highstreet.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEn_9rHtsA1GvHAB30K5O8LQnMASwJtq-c5S9QLSYnUj2SS7HSB_0k4S1echFbXIYhjg9P2FV0NyQGwJLpejCNxsgO0xNkVFkmQx2ZAZw2-RtzhOoXZLPLrwuN2iv64zpKa7yNR85mLrk/&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;711&quot; data-original-width=&quot;1215&quot; height=&quot;187&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEn_9rHtsA1GvHAB30K5O8LQnMASwJtq-c5S9QLSYnUj2SS7HSB_0k4S1echFbXIYhjg9P2FV0NyQGwJLpejCNxsgO0xNkVFkmQx2ZAZw2-RtzhOoXZLPLrwuN2iv64zpKa7yNR85mLrk/&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;And so &lt;a href=&quot;https://highstreet.ly&quot;&gt;highstreet.ly&lt;/a&gt; was born. We are a subscriptions based SaaS offering, providing tools that we hope will allow highstreet businesses to offer services they maybe wouldn&#39;t otherwise be able to at a cost which is far, far less than the other offerings. Our service has three main components:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Shop / widget (for self hosting)&lt;/li&gt;&lt;li&gt;Dashboard&amp;nbsp;&lt;/li&gt;&lt;li&gt;Operator app&lt;/li&gt;&lt;/ul&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4R8VOmsWKa6itJ45OXwSQpsSRgw2pSznudNk7BpISOLJIwSgOEtPOS9FgMeblId9_KQmKMi4nDmmFYRywVGkGm8TqC6doN2z_vLhkGcKQLwG-cfSoVKK3blXISbM-sO8Oiz5T2F5vMVw/s1914/Screenshot+2021-05-14+at+16.59.48.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1084&quot; data-original-width=&quot;1914&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4R8VOmsWKa6itJ45OXwSQpsSRgw2pSznudNk7BpISOLJIwSgOEtPOS9FgMeblId9_KQmKMi4nDmmFYRywVGkGm8TqC6doN2z_vLhkGcKQLwG-cfSoVKK3blXISbM-sO8Oiz5T2F5vMVw/s320/Screenshot+2021-05-14+at+16.59.48.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In order to keep the costs down but still pack a punch we host our own servers in a datacenter in Brighton(FastNet). This cuts our hosting fees into a fraction of what they would be in the cloud.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I&#39;m going to echo what Anthony said in his post: Our architecture is the way it is because it&#39;s what suits us, it was developed over trial and error and reflects my own personal experience and expertise - your solution may / will be vastly different. There is no right or wrong way to archteour system - I&#39;m certainly not able to advice on that. Our system is architected the way it is so that we can streamline our infrastructure, keep our costs down and focus on building the software that makes our product.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I also want to repeat what Anthony said is his post: You can tell by the image below that we use a huge amount of amazing open source software - we really do stand on the shoulders of giants. Making a SaaS product is truly a challenge but it would be pretty much impossible without the amazing work put in by the people behind these projects.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Here&#39;s a birds eye view of our architecture:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUPn3WLAlJX6ZWl4H4AlZIQ0iwrDzWyNcjV4ye1qOMKu6XtbdJKj8GQlC2asYzZa2lBmQe5jbRCRW-isMHQShT5jbFeEbk6N5X8paebsc1ZuOtWBuZxwpKJXm3py9PsbUwhPsjL3blUUs/s2032/system+%25281%2529.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;905&quot; data-original-width=&quot;2032&quot; height=&quot;208&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUPn3WLAlJX6ZWl4H4AlZIQ0iwrDzWyNcjV4ye1qOMKu6XtbdJKj8GQlC2asYzZa2lBmQe5jbRCRW-isMHQShT5jbFeEbk6N5X8paebsc1ZuOtWBuZxwpKJXm3py9PsbUwhPsjL3blUUs/w466-h208/system+%25281%2529.png&quot; width=&quot;466&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;What we got:&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;We have 3 x DL380 servers hosted in the datacenter&lt;/li&gt;&lt;li&gt;We have 1 x SonicWall firewall&amp;nbsp;&lt;/li&gt;&lt;li&gt;The 3 servers each run Esxi (Open Source version) to host the nodes that make up our hosting&lt;/li&gt;&lt;li&gt;1 of the VMs runs Postgres&lt;/li&gt;&lt;li&gt;1 of the VMs runs RabbitMQ&lt;/li&gt;&lt;li&gt;We then have 12 VMs which make up our Kubernetes cluster&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;On all of this we run 3 environments (LIVE, TEST, DEV) - each in their own namespaces with their own databases and vhosts in RMQ.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To provision the cluster I use the amazing KubeSpray (&lt;a href=&quot;https://github.com/kubernetes-sigs/kubespray&quot;&gt;https://github.com/kubernetes-sigs/kubespray&lt;/a&gt;)&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I&#39;ve also added to that our own ansible project to install things after the cluster is initialised - stuff like CertManager etc. We used to host Postgres and RMQ inside the cluster using Rook / Ceph but I found the performance lacking and recovering from a failure was very involved.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Once the cluster is up and running we use Pulumi (&lt;a href=&quot;https://www.pulumi.com&quot;&gt;https://www.pulumi.com&lt;/a&gt;/) to deploy our services. However, this is done via a github action - with a remote runner which lives in our cluster. So pushing our pulumi code with a git tag will trigger a build of our IaC project - which sends the build context to the runner living inside our cluster which then installs all of the services for the namespace being targetted. It sounds complicated but it&#39;s very, very easy once it&#39;s set up and it means I never have to SSH onto any servers to deploy anything.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We have 3 types of containers we need to build to make up our system:&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;DotNetCore API services&lt;/li&gt;&lt;li&gt;Ember / Svelte NGinx containers&lt;/li&gt;&lt;li&gt;Migrations jobs&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;The migrations jobs use FlyWay (&lt;a href=&quot;https://flywaydb.org/&quot;&gt;https://flywaydb.org/&lt;/a&gt;) and work by convention - any new script in the relevant folder is picked up and the job runs. The dotnet core images are built using GitHub actions and stored in Docker Hub and the same for the Ember / Svelte images - except these are compiled in the action and the /release folder is copied into the NGinx image.&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We have 5 bounded contexts and 4 of them have a message receiver process so we have 9 services that run as microservices inside each of our cluster namespaces. We use the CQRS/ES pattern where applicable.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For authentication we use IdentityServer and our microservices run in a zero trust architecture (&lt;a href=&quot;https://www.paloaltonetworks.com/cyberpedia/what-is-a-zero-trust-architecture&quot;&gt;https://www.paloaltonetworks.com/cyberpedia/what-is-a-zero-trust-architecture&lt;/a&gt;). Our UIs use OAuth and JWT tokens to authenticate against our servers.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Because we are using a microservice architecture we also use a BFF setup (&lt;a href=&quot;https://tsh.io/blog/design-patterns-in-microservices-api-gateway-bff-and-more/#:~:text=BFF%20is%20essentially%20a%20variant,multiple%20gateways%20for%20each%20client&quot;&gt;https://tsh.io/blog/design-patterns-in-microservices-api-gateway-bff-and-more/#:~:text=BFF%20is%20essentially%20a%20variant,multiple%20gateways%20for%20each%20client&lt;/a&gt;.) - this allows us to present a specialised API for each of our clients which only presents what is necessary. It also allows us to do things like cahcing and rate limiting. For the BFF we use Ocelot:&amp;nbsp;&lt;a href=&quot;https://github.com/ThreeMammals/Ocelot&quot;&gt;https://github.com/ThreeMammals/Ocelot&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;When we specify a service with an ingress (api&#39;s UI&#39;s etc) - cert-manager automatically provisions the TLS for it - this hangs off of the Pulumi infrastrusture where by Pulumi will create the DNS at Digital Ocean automatically for us based on the configuration of the environment being deployed. We use the cert-manager DNS01 challenge with DO to make this happen:&amp;nbsp;&lt;a href=&quot;https://cert-manager.io/docs/configuration/acme/dns01/digitalocean/&quot;&gt;https://cert-manager.io/docs/configuration/acme/dns01/digitalocean/&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Putting all of this together, we have a fully automated CI/CD pipeline with versioning at every level - even the infrastructure can be rolled back with a single command. This alone is something I see &quot;big budget&quot; projects fail at all of the time. The code and wiring to acheieve this is simple, uses standard. open source tooling - and that matters because as we grow, new developers joining the team will be able to &quot;get&quot; all of this really quickly.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2TaYFeNfvTYReAhulrVWVAvgNKDV47wPWT5Q8P1QY6Ulj5O1gpNcCWluuCXzrWcJyz5tIEGKOi4YhfHz8nZNI6lYAhcmS3ebF9PD_Mu5RlkSfICz78QCxoE1SPgfBxZcitLlzvGSBPEY/&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;594&quot; data-original-width=&quot;985&quot; height=&quot;193&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2TaYFeNfvTYReAhulrVWVAvgNKDV47wPWT5Q8P1QY6Ulj5O1gpNcCWluuCXzrWcJyz5tIEGKOi4YhfHz8nZNI6lYAhcmS3ebF9PD_Mu5RlkSfICz78QCxoE1SPgfBxZcitLlzvGSBPEY/&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For local development we use Kind-Kubernetes&amp;nbsp;https://kind.sigs.k8s.io/docs/user/quick-start/ - the good thing about this is that, to spin up a cluster locally just requires a new Pulumi stack definition. My local cluster uses exaclty the same code to initialise as the deployed, publicly visible environments.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For logging we use NewRelic - this has a log scraper installed inside our cluster which automatically ships our telemetrics to NR for analysis and alerts.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Since we are a subscriptions based service, we currently use ChargeBee (&lt;a href=&quot;https://www.chargebee.com&quot;&gt;https://www.chargebee.com&lt;/a&gt;/) to host our plans and add-ons. We have a webhook set up on our end which recieves events from ChargeBee and allows us to mirror the configuration and react to changes in user subscriptions.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The good thing about ChargeBee is that it enables us to set up the subscriptions infrastructure without needing to implement 100% of it - we can settle that tech debt when we need to. It also integrates with Stripe.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For our shop widget we use Stripe to handle card payments. The good thing about Stripe - for us - is that we can route payments for products purchased to our B2C customers Stripe accounts and our platform fee to our Stripe account. We also plan to allow our B2C customers, customers (!) to subscribe to their products - and Stripe allows this in a very easy and fluid manner.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;That&#39;s a kind of whirl wind tour of the systems behind highstreet.ly! Techies with a keen eye will see there&#39;s one or two elements missing from the picture above (CDN etc) - we&#39;re working towards these things and we will get there. It&#39;s all a part of the challenge! I hope it helps reassure someone else out there in the same way reading Anhony&#39;s post reassured me!&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/7681814443196546691/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2021/05/the-architechture-behind-highstreetly.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/7681814443196546691'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/7681814443196546691'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2021/05/the-architechture-behind-highstreetly.html' title='The Architechture behind highstreet.ly'/><author><name>wayne</name><uri>http://www.blogger.com/profile/00603417641518780876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEn_9rHtsA1GvHAB30K5O8LQnMASwJtq-c5S9QLSYnUj2SS7HSB_0k4S1echFbXIYhjg9P2FV0NyQGwJLpejCNxsgO0xNkVFkmQx2ZAZw2-RtzhOoXZLPLrwuN2iv64zpKa7yNR85mLrk/s72-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-1856489572709390835</id><published>2020-06-12T08:36:00.010-07:00</published><updated>2020-06-15T08:48:36.038-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="consul"/><category scheme="http://www.blogger.com/atom/ns#" term="ddd/cqrs"/><category scheme="http://www.blogger.com/atom/ns#" term="kubernetes"/><category scheme="http://www.blogger.com/atom/ns#" term="minikube"/><category scheme="http://www.blogger.com/atom/ns#" term="pulumi"/><title type='text'>It&#39;s been a long time baby!</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoSLbsaETkejtEEQStK7CYBPbNjzbN7bReojJeSQ-0GmwIn-zlW8GS5yAXoDf7PM3hJy5_811MsWkgdzRV76CzBLn52vsHx7Hztyagho6KYtVcs8gV3ezUSyBnRbD2v-4XrfI3ytyr79c/s1000/st%252Csmall%252C845x845-pad%252C1000x1000%252Cf8f8f8.u3.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1000&quot; data-original-width=&quot;1000&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoSLbsaETkejtEEQStK7CYBPbNjzbN7bReojJeSQ-0GmwIn-zlW8GS5yAXoDf7PM3hJy5_811MsWkgdzRV76CzBLn52vsHx7Hztyagho6KYtVcs8gV3ezUSyBnRbD2v-4XrfI3ytyr79c/s320/st%252Csmall%252C845x845-pad%252C1000x1000%252Cf8f8f8.u3.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;font face=&quot;verdana&quot;&gt;It&#39;s been a while (5 years!) so I thought I&#39;d start up the old blog again - see where it goes!&lt;/font&gt;&lt;div&gt;&lt;font face=&quot;verdana&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font face=&quot;verdana&quot;&gt;To kick things off I thought I&#39;d do a couple of videos showing off the sona* stack as it stands today. YES we are STILL working in our spare time on the sona* dream!&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font face=&quot;verdana&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font face=&quot;verdana&quot;&gt;It&#39;s currently being built as a ticketing / delivery platform. Depending on the options selected during deployment it will run as either a ticketing platform or a delivery / online shop app. The main focus atm is the delivery side - allowing local shops a cost effective way to sell products and deliver them to customers amid the pandemic.&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font face=&quot;verdana&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font face=&quot;verdana&quot;&gt;This video kicks things off by showing off some of our devops stuff. It shows our use of things like Kubernetes (full blown and developer minikube installations), Ansible, Pulumi, a home made CLI written in NodeJs, Consul and some other goodies.&amp;nbsp;&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font face=&quot;verdana&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font face=&quot;verdana&quot;&gt;The system is still written with a CQRS/ES backend written in C# and uses a mixture of EmberJs, Svelte and Xamarin for the front-ends. And they say full stack doesn&#39;t exist? ;)&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&#39;allowfullscreen&#39; webkitallowfullscreen=&#39;webkitallowfullscreen&#39; mozallowfullscreen=&#39;mozallowfullscreen&#39; width=&#39;320&#39; height=&#39;266&#39; src=&#39;https://www.blogger.com/video.g?token=AD6v5dyTsXyIKujajsutyugegYWm76Fqzb5zjwWDE64NOWkI-DnxMyMdItr93Ru0JSFoUyDdmlv4YA229xXET_LHpA&#39; class=&#39;b-hbp-video b-uploaded&#39; frameborder=&#39;0&#39;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/1856489572709390835/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2020/06/its-been-long-time-baby.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/1856489572709390835'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/1856489572709390835'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2020/06/its-been-long-time-baby.html' title='It&#39;s been a long time baby!'/><author><name>wayne</name><uri>http://www.blogger.com/profile/00603417641518780876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoSLbsaETkejtEEQStK7CYBPbNjzbN7bReojJeSQ-0GmwIn-zlW8GS5yAXoDf7PM3hJy5_811MsWkgdzRV76CzBLn52vsHx7Hztyagho6KYtVcs8gV3ezUSyBnRbD2v-4XrfI3ytyr79c/s72-c/st%252Csmall%252C845x845-pad%252C1000x1000%252Cf8f8f8.u3.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-336832193140347486</id><published>2015-09-23T06:54:00.002-07:00</published><updated>2015-09-23T06:54:39.349-07:00</updated><title type='text'>Jwt Authentication with Ember + SailsJs / Waterlock Part 2 - Google oAuth2</title><content type='html'>Leading on from part 1&amp;nbsp;&lt;a href=&quot;http://iwayneo.blogspot.co.uk/2015/09/jwt-authentication-with-ember-sailsjs.html&quot; target=&quot;_blank&quot;&gt;Jwt Authentication with Ember + SailsJs / Waterlock&lt;/a&gt;&amp;nbsp;I decided to continue with the theme by adding the ability to authenticate with a 3rd party authentication service - in this example I&#39;m using Google oAuth2.&lt;br /&gt;
&lt;br /&gt;
Now this wasn&#39;t simple for me to figure out initially! Partly because, in my mind, the confusing docs surrounding Google oAuth2 where they state:&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
When a user grants access to your app for a particular scope, the user is looking at the user consent screen, which includes project-level product branding that you set up in the Google Developers Console. (For information about setting up the consent screen, see Setting up OAuth 2.0 in the Developers Console help.) Therefore, Google considers that when a user has granted access to a particular scope to any client ID in a project, the grant indicates the user&#39;s trust in the whole application for that scope.&lt;/blockquote&gt;
Now, I read that as: when you collect an authentication code from one medium within your applications &quot;product&quot; suite, provided other parts of that suite use a client ID that share the same &quot;project&quot; at Google&#39;s end - then you can use that client and authentication code to gain an access token - and thus validate the user. Apparently not so! I spent ages going round in hoops over this and settled on a simple setup which means I can authenticate the user on both the client (Ember) and server (Sails).&lt;br /&gt;
&lt;br /&gt;
To make things clear - the idea of all this is to provide a sensible UX for a browser application to authenticate using 3rd party auth services. This includes having the auth flow inside the application and as we are an SPA we don&#39;t want to have any nasty page refreshes or redirects (in the main browser). I am pulling in a few other libraries to achieve this and have had to modify the Waterlock library slightly to enable this to work. I&#39;ll be cleaning that up and pushing back to Waterlock soon but for now I have included the deps as submodules inside our repo.&lt;br /&gt;
&lt;br /&gt;
The desired flow is:&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;User clicks &quot;authenticate with Google&quot; button&lt;/li&gt;
&lt;li&gt;Popup appears with Google&#39;s auth flow&lt;/li&gt;
&lt;li&gt;User provides access to our app&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Popup closes (main app never refreshes or redirects)&lt;/li&gt;
&lt;li&gt;Our app receives the authorization code from Google&lt;/li&gt;
&lt;li&gt;Sends that code to our API&lt;/li&gt;
&lt;li&gt;Our Api then validates that code by exchanging it for an access token&lt;/li&gt;
&lt;li&gt;If the code is valid we find or create a user and set up the JWT for the client app&lt;/li&gt;
&lt;li&gt;Our client app receives the JWT and uses that for future requests to the server&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
Quite long winded but the net result is a great UX for our users and a reliable authentication flow that&#39;s secure. Now - one major change that I have had to make here to make this work - baring in mind that I have no idea how to get &quot;Cross Client Authorization&quot; to work - was to change the way the app access the API. To enable this I am proxying the API through ember. You can achieve this by running the following command (instead of just running &quot;ember server&quot;):&lt;/div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
ember server --proxy http://localhost:1337&lt;/blockquote&gt;
Simple!&lt;br /&gt;
&lt;br /&gt;
In terms of changes - I have included &amp;nbsp;a modified version of &lt;a href=&quot;https://github.com/waterlock/waterlock-google-auth&quot; target=&quot;_blank&quot;&gt;waterlock-google-auth&lt;/a&gt; in the API - this has a simple setup in /config/waterlock.js - you can see the required bits in my repo and by reading the readme on the main repo. The changes I had to implement were to ommit the requirement of the client to provide a &lt;a href=&quot;https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)&quot; target=&quot;_blank&quot;&gt;CSRF&lt;/a&gt;&amp;nbsp;- I may add a step where the client can retreieve one before starting the auth flow - I may omit it by using a separate endpoint for SPA auth - not sure of the implications yet. That&#39;s all for the server!&lt;br /&gt;
&lt;br /&gt;
The client has a few new pieces:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;I&#39;ve included the &lt;a href=&quot;https://github.com/simplabs/ember-cli-simple-auth-torii&quot; target=&quot;_blank&quot;&gt;ember-cli-simple-auth-torii&lt;/a&gt; package - this handles auth flows requiring a popup&lt;/li&gt;
&lt;li&gt;I&#39;ve also included the&amp;nbsp;&lt;a href=&quot;https://github.com/simplabs/ember-cli-simple-auth-oauth2&quot; target=&quot;_blank&quot;&gt;ember-cli-simple-auth-oauth2&lt;/a&gt; package&lt;/li&gt;
&lt;li&gt;And finally the&amp;nbsp;&lt;a href=&quot;https://github.com/Vestorly/torii&quot; target=&quot;_blank&quot;&gt;torii&lt;/a&gt;&amp;nbsp;package.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
Because we are customizing the standard flow a tad (sending the auth code to the API) I had to implement a custom authenticator:&amp;nbsp;&lt;a href=&quot;https://github.com/wayne-o/ember-waterlock-example/blob/master/sonatribe-ui/app/authenticators/authenticate-google.js&quot; target=&quot;_blank&quot;&gt;https://github.com/wayne-o/ember-waterlock-example/blob/master/sonatribe-ui/app/authenticators/authenticate-google.js&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The interesting part &lt;a href=&quot;https://github.com/wayne-o/ember-waterlock-example/blob/master/sonatribe-ui/app/authenticators/authenticate-google.js#L14&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; is where I exchange the auth code for a validated user with an access token provided by our server - this makes up our JWT! Nice.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
I&#39;ll likely move on to add Facebook authentication and possibly create our own Spotify add-on to Waterlock and wire that in. Finally I&#39;m planning on adding web sockets to provide &quot;live&quot; UI updates.&lt;/div&gt;
&lt;br /&gt;
</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/336832193140347486/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2015/09/jwt-authentication-with-ember-sailsjs_23.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/336832193140347486'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/336832193140347486'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2015/09/jwt-authentication-with-ember-sailsjs_23.html' title='Jwt Authentication with Ember + SailsJs / Waterlock Part 2 - Google oAuth2'/><author><name>wayne</name><uri>http://www.blogger.com/profile/00603417641518780876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-1978455223234947494</id><published>2015-09-18T08:29:00.000-07:00</published><updated>2015-09-21T02:49:54.810-07:00</updated><title type='text'>Jwt Authentication with Ember + SailsJs / Waterlock</title><content type='html'>Thought I&#39;d knock together a little example of using Waterlock/SailsJs with EmberJs and using JWT as the mechanism.&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
JSON Web Token (JWT) is a JSON-based open standard (RFC 7519) for passing claims between parties in web application environment. The tokens are designed to be compact, URL-safe and usable especially in web browser single sign-on (SSO) context.&lt;/blockquote&gt;
JWT is a means of providing a simple claims based authentication between a client and a server. The token is an encrypted piece of data which can be sent as part of a payload to allow access to restricted services. The full RFC is here&amp;nbsp;&lt;a href=&quot;https://tools.ietf.org/html/rfc7519&quot;&gt;https://tools.ietf.org/html/rfc7519&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
The basic gist of JWT is this:&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;User authenticates with server (identity/password, oAuth etc)&lt;/li&gt;
&lt;li&gt;Server generates JWT using secret key and some payload (usually a JSON string that provides some basic identification information, a subset of the User object for instance)&lt;/li&gt;
&lt;li&gt;Server responds with JWT access_token&lt;/li&gt;
&lt;li&gt;Client stores access_token in cookie or local storage (we will be using local storage)&lt;/li&gt;
&lt;li&gt;Client provides access_token in future requests.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
While this may look a lot like the standard cookies session ID flow there is a major difference in that a JWT is a small encrypted package which defines the access claim and identifies the user. They are inherently much more secure than cookies as well as allowing statelessness - and this is the big one. Yeah sure we can manage session state across servers in a cluster using a central cache like Redis. Go ahead, hope you have a nice day breaking the fundamental design of the web.&amp;nbsp;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
Waterlock is a nifty little user authentication//JSON web token management tool built for Sails:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Waterlock - &lt;a href=&quot;http://waterlock.ninja/&quot;&gt;http://waterlock.ninja/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;SailsJs - &lt;a href=&quot;http://sailsjs.org/&quot;&gt;http://sailsjs.org/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
Using these two projects in tandem makes it ridiculously easy to get up and running with JWT and a decent REST API. Couple that with EmberJs - &quot;a framework for ambitious web applications&quot; and you have a pretty niffty set-up.&lt;br /&gt;
&lt;br /&gt;
The best way for handling auth in an Ember app is through the use of Ember Simple Auth:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Ember Js - &lt;a href=&quot;http://emberjs.com/&quot;&gt;http://emberjs.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Ember Simple Auth - &lt;a href=&quot;http://ember-simple-auth.com/&quot;&gt;http://ember-simple-auth.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
While all of these parts are really simple to use they aren&#39;t necessarily that simple to get up and running with. This whole area of software development (JavaScript in particular) is evolving at a rate of knots, &amp;nbsp;JWT is a relatively new technology and server side frameworks such as SailsJs and client side frameworks like EmberJs are moving so fast it&#39;s really, really hard to keep up unless you&#39;re lucky enough to be able to dedicate your full attention! This is both good an bad news, there&#39;s a lot of well meaning blog posts out there explaining how to do XYZ with these tools but as soon as they are published they&#39;re out of date. I expect the same will unfortunately be true of this post. But for now at least the repo is using the very latest of each of the dependancies and (I hope) provides some clear guidance in getting this set up.&lt;/div&gt;
&lt;br /&gt;
So that&#39;s the server side and UI components. I&#39;m not going to go through setting up each of these components - you can see how that&#39;s all done on each of their repos. I&#39;m also asuming that if you&#39;re interested in this little cocktail of code you&#39;re probably aware of each of them but a little confused as to how you go about pinning it all together.&lt;br /&gt;
&lt;br /&gt;
So down to the code! The repo is here: &lt;a href=&quot;https://github.com/wayne-o/ember-waterlock-example&quot;&gt;https://github.com/wayne-o/ember-waterlock-example&lt;/a&gt; go ahead and clone it if that&#39;s what flicks your switch - I expect it does else you wouldn&#39;t have read this far.&lt;br /&gt;
&lt;br /&gt;
There&#39;s 2 parts to this repo:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;The Server&lt;/li&gt;
&lt;li&gt;The Client&lt;/li&gt;
&lt;/ul&gt;
I&#39;ll walk us through the interesting parts of the server initially - there&#39;s not much to it!&lt;br /&gt;
&lt;br /&gt;
I&#39;ve added a couple of extras here that help make things simple. Ember-Data is a brilliant little library - it doesn&#39;t do everything and I don&#39;t use it everywhere but where it is used it&#39;s very clever and ahead of it&#39;s time. One of those areas is being a pretty complete implementation of the JSON-API spec: &lt;a href=&quot;http://jsonapi.org/&quot;&gt;http://jsonapi.org/&lt;/a&gt;. And to get SailsJs dancing nicely with this spec we need to update the blueprints. To do this I use the brilliant repo by mphasize: &lt;a href=&quot;https://github.com/mphasize/sails-ember-blueprints&quot;&gt;https://github.com/mphasize/sails-ember-blueprints&lt;/a&gt;. What this does is turn the standard JSON output from sails into nice neat JSON API compatible JSON. Neat.&lt;br /&gt;
&lt;br /&gt;
I&#39;ve also included waterlock and waterlock-local-auth. To get all these you just need to cd into the /sonatribe-api directory and run&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
npm install&lt;/blockquote&gt;
Now - that&#39;s pretty much it - if you&#39;re doing these from fresh you&#39;ll need to run the generate blueprints command but that&#39;s all documented on the repo&#39;s readme.md - RTFM ;)&lt;br /&gt;
&lt;br /&gt;
The noteworthy config changes include in the /config/models.js file:&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://github.com/wayne-o/ember-waterlock-example/blob/master/sonatribe-api/config/models.js&quot;&gt;https://github.com/wayne-o/ember-waterlock-example/blob/master/sonatribe-api/config/models.js&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
You can see that I&#39;ve added the &quot;associations&quot; and &quot;validations&quot; nodes to the config. It just defines explicitly how you want these types of nodes to appear in the JSON output.&lt;br /&gt;
&lt;br /&gt;
Next up is waterlock.js:&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://github.com/wayne-o/ember-waterlock-example/blob/master/sonatribe-api/config/waterlock.js&quot;&gt;https://github.com/wayne-o/ember-waterlock-example/blob/master/sonatribe-api/config/waterlock.js&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
You&#39;re going to need to tell it to pluralizeEndpoints and also you need to rename the token name - ember-simple-auth expects &quot;access_token&quot; - don&#39;t do this and you&#39;re into a world of pain. You also want to set &quot;stateless&quot; to false - we don&#39;t want any nasty sessions on the server thank you please. Sessions are grotty at the best of time and mess with your head when you try and step up in the scalability realm. JWT is stateless authentication. It keeps things clean.&lt;br /&gt;
&lt;br /&gt;
That&#39;s it for the API! Simple yeah.&lt;br /&gt;
&lt;br /&gt;
So now onto the client - this is where things can get a little... involved. But considering what you&#39;re getting - I&#39;d say not too involved. You&#39;ll want to cd into the /sonatribe-ui folder and do the usual npm install &amp;amp;&amp;amp; bower install stuff.&lt;br /&gt;
&lt;br /&gt;
I&#39;ve included ember-cli-simple-auth and ember-cli-simple-auth-token which are set up in the /config/environment.js file: &lt;a href=&quot;https://github.com/wayne-o/ember-waterlock-example/blob/master/sonatribe-ui/config/environment.js&quot;&gt;https://github.com/wayne-o/ember-waterlock-example/blob/master/sonatribe-ui/config/environment.js&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Go with my defaults for now - you can mess with these later if you want.&lt;br /&gt;
&lt;br /&gt;
Things to note:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;I&#39;ve used pod structure - it&#39;s great for organising an app small or large.&lt;/li&gt;
&lt;li&gt;I&#39;ve used the new computed decorators: &lt;a href=&quot;https://github.com/rwjblue/ember-computed-decorators&quot;&gt;https://github.com/rwjblue/ember-computed-decorators&lt;/a&gt; meaning we get Java style attribute decoration for things like @observe and @property and even @whateverYouWant&lt;/li&gt;
&lt;/ul&gt;
All this works out of the box when you npm/bower install my repo. The login and register components can be found here &lt;a href=&quot;https://github.com/wayne-o/ember-waterlock-example/tree/master/sonatribe-ui/app/pods/components&quot;&gt;https://github.com/wayne-o/ember-waterlock-example/tree/master/sonatribe-ui/app/pods/components&lt;/a&gt; - we let the framework do the work so these components are ridiculously simple:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Login: &lt;a href=&quot;https://github.com/wayne-o/ember-waterlock-example/blob/master/sonatribe-ui/app/pods/components/login/login-panel/component.js&quot;&gt;https://github.com/wayne-o/ember-waterlock-example/blob/master/sonatribe-ui/app/pods/components/login/login-panel/component.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Register: &lt;a href=&quot;https://github.com/wayne-o/ember-waterlock-example/blob/master/sonatribe-ui/app/pods/components/register/register-panel/component.js&quot;&gt;https://github.com/wayne-o/ember-waterlock-example/blob/master/sonatribe-ui/app/pods/components/register/register-panel/component.js&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
The only noteworthy thing being the slightly different flow for registering as opposed to loging in - waterlock (currently) uses the same workflow and if an un-registered user attempts to login and &quot;createOnNotFound&quot; (&lt;a href=&quot;https://github.com/wayne-o/ember-waterlock-example/blob/master/sonatribe-api/config/waterlock.js#L48&quot;&gt;https://github.com/wayne-o/ember-waterlock-example/blob/master/sonatribe-api/config/waterlock.js#L48&lt;/a&gt;) is true, waterlock assumes you want to create a new users - and provided the dupe checking passes (no duplicate username/email validations) - a new user is created. In future versions of Waterlock there will be an explicit /register endpoint. For now it&#39;s best to query the /users endpoint for email / username availability. I&#39;m doing this here: &lt;a href=&quot;https://github.com/wayne-o/ember-waterlock-example/blob/master/sonatribe-ui/app/pods/components/register/register-panel/component.js#L10&quot;&gt;https://github.com/wayne-o/ember-waterlock-example/blob/master/sonatribe-ui/app/pods/components/register/register-panel/component.js#L10&lt;/a&gt; (not wired up atm - I&#39;ll likely do that in the week to finish up).&lt;br /&gt;
&lt;br /&gt;
So the process is to call /login if the validations for username / email pass - get the user registered and logged in and then, using the response with our authentication data we can populate the users User model and save. Finally we push the user off to the main app by sending an action to the parent route (&quot;userAuthenticated&quot;).&lt;br /&gt;
&lt;br /&gt;
I&#39;m aware that because I am accessing Ember-Data and JQ.Ajax within this component I am completely side stepping DDAU (http://www.samselikoff.com/blog/data-down-actions-up/) - and this is a bit cringe but I&#39;m going to leave it as is as I want the example to be clear. The simplicity of using these libraries side by side should be unhindered!&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/1978455223234947494/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2015/09/jwt-authentication-with-ember-sailsjs.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/1978455223234947494'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/1978455223234947494'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2015/09/jwt-authentication-with-ember-sailsjs.html' title='Jwt Authentication with Ember + SailsJs / Waterlock'/><author><name>wayne</name><uri>http://www.blogger.com/profile/00603417641518780876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-235817046207763915</id><published>2015-09-16T05:59:00.000-07:00</published><updated>2015-09-16T05:59:12.363-07:00</updated><title type='text'>C# Threading Primers</title><content type='html'>I&#39;ve been refreshing my brain on threading in C# recently and have compiled a list of resources I found excellent on the subject.&lt;br /&gt;
&lt;br /&gt;
I&#39;ll leave them here for future me and also for anyone interested in the topic - save some google bandwidth :)&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Joe Albahari - Threading in C# &lt;a href=&quot;http://www.albahari.com/threading/&quot;&gt;http://www.albahari.com/threading/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Async Programming : Unit Testing Asynchronous Code: Three Solutions for Better Tests - &lt;a href=&quot;https://msdn.microsoft.com/en-gb/magazine/dn818494.aspx?f=255&amp;amp;MSPPError=-2147217396&quot;&gt;https://msdn.microsoft.com/en-gb/magazine/dn818494.aspx?f=255&amp;amp;MSPPError=-2147217396&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Async/Await - Best Practices in Asynchronous Programming - &lt;a href=&quot;https://msdn.microsoft.com/en-us/magazine/jj991977.aspx&quot;&gt;https://msdn.microsoft.com/en-us/magazine/jj991977.aspx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;Talk: Async best practices - &lt;a href=&quot;http://blogs.msdn.com/b/lucian/archive/2013/11/23/talk-mvp-summit-async-best-practices.aspx&quot;&gt;http://blogs.msdn.com/b/lucian/archive/2013/11/23/talk-mvp-summit-async-best-practices.aspx&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;
</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/235817046207763915/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2015/09/c-threading-primers.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/235817046207763915'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/235817046207763915'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2015/09/c-threading-primers.html' title='C# Threading Primers'/><author><name>wayne</name><uri>http://www.blogger.com/profile/00603417641518780876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-992227079898322068</id><published>2014-10-20T10:17:00.000-07:00</published><updated>2014-10-20T10:19:58.139-07:00</updated><title type='text'>Working with .NET on ubuntu</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://www.vagrantup.com/images/logo_vagrant-81478652.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://www.vagrantup.com/images/logo_vagrant-81478652.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
If you&#39;re a .NET dev interested in finding out what it&#39;s like to develop .NET apps on Ubuntu the cross over can be quite painful. I&#39;ve created a vagrant box which comes loaded with the bleeding edge MonoDevelop (actually compiled from the source).&lt;br /&gt;
&lt;br /&gt;
To get started you need &lt;a href=&quot;https://www.vagrantup.com/&quot;&gt;vagrant&lt;/a&gt; installed. You can run this on any OS: OSX, Windows or Ubuntu. You&#39;ll also need &lt;a href=&quot;https://www.virtualbox.org/&quot;&gt;VirtualBox&lt;/a&gt; - both free and OS :)
&lt;br /&gt;
Then just pull my vagrant config from the &lt;a href=&quot;https://github.com/wayne-o/vagrant-monodevelop/tree/master&quot;&gt;repo&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
All you need to do then is open a terminal in that directory and type:&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: #999999;&quot;&gt;vagrant up&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
BOOM!&lt;br /&gt;
&lt;br /&gt;
It takes a while to get loaded 1st time as it builds MonoDevelop from source but it&#39;s all set up and ready to go when it&#39;s finished.&lt;br /&gt;
&lt;br /&gt;
Vagrant is a great tool, lots of NET developers might not have heard of it but it makes creating dev (and prod) environments super easy.&lt;br /&gt;
&lt;br /&gt;
Enjoy :)</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/992227079898322068/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2014/10/working-with-net-on-ubuntu.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/992227079898322068'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/992227079898322068'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2014/10/working-with-net-on-ubuntu.html' title='Working with .NET on ubuntu'/><author><name>wayne</name><uri>http://www.blogger.com/profile/00603417641518780876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-2978168083568812655</id><published>2014-10-04T01:37:00.001-07:00</published><updated>2014-10-04T01:55:00.057-07:00</updated><title type='text'>Hosting an ember-cli app inside a ServiceStack (or any) MVC app</title><content type='html'>The resulting code for this post can be found at:&amp;nbsp;&lt;a href=&quot;https://github.com/wayne-o/bower-link-test&quot; target=&quot;_blank&quot;&gt;https://github.com/wayne-o/bower-link-test&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
We are looking into refactoring sonatribe to be an ember-cli SPA. Using the Discourse code base as inspiration I&#39;ve been spiking various parts to see exactly how feasible this would be.&lt;br /&gt;
&lt;br /&gt;
This all started with us wanting to mimic the discourse header (ignoring the messy sub header):&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFIh9r-p5L8yF45YHu8y1N_YjJFKxOW4SYac2bGKA0ASIibqDEtB6NTrtxX3Nk6MJcH14j3529nNYW6n3sDDsJn7ZMD-Ve9iuQcol6UqM_QoQmNDr08h-v5BQpXnScoYkkJv91zVIgwUg/s1600/Screenshot+from+2014-10-04+08:50:50.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFIh9r-p5L8yF45YHu8y1N_YjJFKxOW4SYac2bGKA0ASIibqDEtB6NTrtxX3Nk6MJcH14j3529nNYW6n3sDDsJn7ZMD-Ve9iuQcol6UqM_QoQmNDr08h-v5BQpXnScoYkkJv91zVIgwUg/s1600/Screenshot+from+2014-10-04+08:50:50.png&quot; height=&quot;228&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
As you can see the header is a really nice, dock-able UI and has a lot of features. My initial thoughts after looking at the code to isolate the header were that this would be near on impossible. We wanted to do this because we want the transition from sonatribe to our discourse installation to be seamless - it&#39;s important that it feels like one app. Ignoring the obvious integration points outside of UI (things like updating sonatribe or discourse when the user receives a PM - this will come later hopefully in the form of plugins). I&#39;d never used Ember or Ruby before and so diving into the Discourse source code was a little daunting!&lt;br /&gt;
&lt;br /&gt;
Reverse engineering the code I managed to stub a lot of the code for things like preload store and bound-avatar and basically get a functioning version of the header isolated into it&#39;s own ember-cli project:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDyXF_Icl4gCvwbf74NZLLIUBvfYGlbj0sQjKPVOB0G7tpeXOEkA7MHGLiinbzC7_r4dyigKJL1GXDyL7wh16_4zWdJ1LsPx974_0G9dZvOisfrhZGt68OpdR4AEww207ASz1y1GrYNJ4/s1600/st-header.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDyXF_Icl4gCvwbf74NZLLIUBvfYGlbj0sQjKPVOB0G7tpeXOEkA7MHGLiinbzC7_r4dyigKJL1GXDyL7wh16_4zWdJ1LsPx974_0G9dZvOisfrhZGt68OpdR4AEww207ASz1y1GrYNJ4/s1600/st-header.png&quot; height=&quot;82&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
Not bad! Wiring this up to our own search API I actually managed to get the grouped search results working. You can see this working here:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;iframe allowfullscreen=&#39;allowfullscreen&#39; webkitallowfullscreen=&#39;webkitallowfullscreen&#39; mozallowfullscreen=&#39;mozallowfullscreen&#39; width=&#39;320&#39; height=&#39;266&#39; src=&#39;https://www.youtube.com/embed/ujDnDRsvF8w?feature=player_embedded&#39; frameborder=&#39;0&#39;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
The search API is a really poor knocked-together ServiceStack API which has zero optimizations so ignore the poor perf on the results!&lt;br /&gt;
&lt;br /&gt;
All well and good but I was still stubbing the preload store. This is where Discourse have done the clever thing of loading the app with all of the data it needs to render itself on boot. Looking at the Discourse HTML you can see there is a lot of JS/JSON injected in. The problem here is that ember-cli doesn&#39;t really work this way. Ember-cli gives you the folder structure to develop in and uses a filesystem watcher to recompile your code (ES6, SCSS etc) into a distributable form. It even serves the index.html inside a HTTP server for you.&lt;br /&gt;
&lt;br /&gt;
Discourse works this way because the SPA knows you&#39;re logged in through the presence of a user object injected into the preload store. They use omniauth which loads a popped browser window to handle the auth mechanism - once the user has authenticated it calls into a hook (window.opener.Discourse.authenticationComplete) to notify discourse that authentication has completed and sends the result. If the user successfully authenticated the browser is reloaded and because the user is authenticated the users JSON is loaded into the preload store.&lt;br /&gt;
&lt;br /&gt;
So, serving the index.html as a dynamic (is that still the right term? It sounds so ASP 3.0!!!) page is pretty much a must have.&lt;br /&gt;
&lt;br /&gt;
To do this we are going to need to drop the HTTP server provided by ember-cli and work out a way to push the /dist folder to another folder which can be part of a hosted project. I&#39;m a big fan of ServiceStack but the hosted project could literally be any language/framework.&lt;br /&gt;
&lt;br /&gt;
The idea here is to have 2 projects:&lt;br /&gt;
&lt;span id=&quot;goog_255874629&quot;&gt;&lt;/span&gt;&lt;span id=&quot;goog_255874630&quot;&gt;&lt;/span&gt;&lt;a href=&quot;https://www.blogger.com/&quot;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;ember-cli app where we can develop the JS and CSS&lt;/li&gt;
&lt;li&gt;an integration project where we can host index.html and inject session state and preloaded data&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
To keep the integration project up-to-date when we make changes in the ember-cli project we can use Bower link (&lt;a href=&quot;http://bower.io/docs/api/#link&quot; target=&quot;_blank&quot;&gt;http://bower.io/docs/api/#link&lt;/a&gt;) - this will replicate changes to the integration project live using symlinks. You can link the /dist folder to the target project - you can see this here:&amp;nbsp;&lt;a href=&quot;https://github.com/wayne-o/bower-link-test/blob/master/BowerLinked/BowerLinked/bower_components/assets&quot; target=&quot;_blank&quot;&gt;https://github.com/wayne-o/bower-link-test/blob/master/BowerLinked/BowerLinked/bower_components/assets&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Rebuilding the project on changes can be done using the &lt;span style=&quot;color: #666666; font-family: Courier New, Courier, monospace;&quot;&gt;ember build --watch&lt;/span&gt; command.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
What this will do is - every time you edit some code in the ember-cli project - ember will notice the files have changed and re-build the code - it then copies the resulting build to the /dist folder. Bower&#39;s symlink will then copy the code to the integration project.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
We can then host our own contents of index.html as a view inside our hosted environment: &lt;a href=&quot;https://github.com/wayne-o/bower-link-test/blob/master/BowerLinked/BowerLinked/Views/Home/Index.cshtml&quot; target=&quot;_blank&quot;&gt;https://github.com/wayne-o/bower-link-test/blob/master/BowerLinked/BowerLinked/Views/Home/Index.cshtml&lt;/a&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
So now we get the mixture of serverside code inside the ember-cli index.html and we can inject in session state and preloaded data!&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
This means I can go back to my header spike project and replace all of the stubbed code with real user data and start to really figure out the auth aspects of this refactoring.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
While this is a ridiculously simple example - the project in github does show this all working:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkUkdQ7GSSflyw8ExzFwB0LaaT-x4R_iVFxvJnVgT3PPYaUPCLWRN9uOUBdHPyKMkBz02ThwxkJgYo9y975xKiuQcyqI_Q7Zlti4fFYwGWtPh9vK4ItbWYHejGC8-SlGB97vFnRNBnPwY/s1600/example.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkUkdQ7GSSflyw8ExzFwB0LaaT-x4R_iVFxvJnVgT3PPYaUPCLWRN9uOUBdHPyKMkBz02ThwxkJgYo9y975xKiuQcyqI_Q7Zlti4fFYwGWtPh9vK4ItbWYHejGC8-SlGB97vFnRNBnPwY/s1600/example.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;br /&gt;
The &quot;this is a test&quot; lines are coming from the cshtml and the ember app is booted below it! All I need to do now is get the authed user from ServiceStack and serialize whatever data we need in the UI into the preload store.&lt;br /&gt;
&lt;br /&gt;
w://&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/2978168083568812655/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2014/10/hosting-ember-cli-app-inside.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/2978168083568812655'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/2978168083568812655'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2014/10/hosting-ember-cli-app-inside.html' title='Hosting an ember-cli app inside a ServiceStack (or any) MVC app'/><author><name>wayne</name><uri>http://www.blogger.com/profile/00603417641518780876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFIh9r-p5L8yF45YHu8y1N_YjJFKxOW4SYac2bGKA0ASIibqDEtB6NTrtxX3Nk6MJcH14j3529nNYW6n3sDDsJn7ZMD-Ve9iuQcol6UqM_QoQmNDr08h-v5BQpXnScoYkkJv91zVIgwUg/s72-c/Screenshot+from+2014-10-04+08:50:50.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-2776971061050654141</id><published>2014-09-07T07:12:00.002-07:00</published><updated>2014-09-07T07:12:43.250-07:00</updated><title type='text'>Running ServiceStack / RavenDb API on Ubuntu</title><content type='html'>I&#39;ve been playing about with Docker and Vagrant a lot recently, I find them to be really amazing tools. Obviously with the amount of OS envy streaming out of my little foray with Docker in particular I am trying to move the Sonatribe API over to run on Mono inside a docker container.&lt;br /&gt;
&lt;br /&gt;
ServiceStack runs great self hosted on Mono but I have been getting issues with RavenDb: async calls seem to fail and things like bulk inserts seem to fail with this:&amp;nbsp;http://stackoverflow.com/questions/13859467/ravendb-client-onlinux-connecting-to-windows-server-using-mono-http/13866929#comment40187983_13866929&lt;br /&gt;
&lt;br /&gt;
If anyone has any help or pointers on these i&#39;d be massively grateful!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/2776971061050654141/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2014/09/running-servicestack-ravendb-api-on.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/2776971061050654141'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/2776971061050654141'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2014/09/running-servicestack-ravendb-api-on.html' title='Running ServiceStack / RavenDb API on Ubuntu'/><author><name>wayne</name><uri>http://www.blogger.com/profile/00603417641518780876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-3591054308602095404</id><published>2014-07-30T06:43:00.001-07:00</published><updated>2014-07-30T06:43:27.378-07:00</updated><title type='text'>Sonatribe conversations platform - CQRS knockout - part 2: GetEventStore.com</title><content type='html'>Following on from part 1 (&lt;a href=&quot;http://iwayneo.blogspot.co.uk/2014/07/sonatribe-conversations-platform-cqrs.html&quot;&gt;CQRS Journey&lt;/a&gt;) this is my implementation spike using GetEventStore. Assesing which CQRS setup we should use at sonatribe for the new conversations feature.&lt;br /&gt;
&lt;br /&gt;
The code can be found &lt;a href=&quot;https://github.com/wayne-o/ges-tryout&quot;&gt;here&lt;/a&gt;.

&lt;br /&gt;
&lt;br /&gt;
This implementation is quite a bit different so I&#39;ll discuss where they branch off from each other and maybe try and figure out the pros and cons.&lt;br /&gt;
&lt;br /&gt;
Using EventStore as the persistence mechanism for storing event streams takes a lot of faff out of the process. For instance if you don&#39;t want to you don&#39;t need to have any service bus sitting between the API -&amp;gt; domain service and read model. However - EventStore is a more lower level technology and because of that you don&#39;t get so much out of the box as you do with CQRS Journey.&lt;br /&gt;
&lt;br /&gt;
With this implementation I decided to keep the domain service in a separate process at the other end of a RabbitMQ queue. So - looking at the API you won&#39;t see a huge amount of difference in terms of complexity (except maybe RMQ is heaps simpler to get up and running).&lt;br /&gt;
&lt;br /&gt;
Moving on from there I have a &amp;nbsp;console app (as opposed to a worker role) running with TopShelf (for easy promotion to Windows Service when required). The code for this is here:&amp;nbsp;&lt;a href=&quot;https://github.com/wayne-o/ges-tryout/blob/master/src/Conversations.DomainService/Program.cs&quot; target=&quot;_blank&quot;&gt;https://github.com/wayne-o/ges-tryout/blob/master/src/Conversations.DomainService/Program.cs&lt;/a&gt;. Really simple way of wiring up the command handlers using Castle Windsor.&lt;br /&gt;
&lt;br /&gt;
Looking at the one and only command handler:&amp;nbsp;&lt;a href=&quot;https://github.com/wayne-o/ges-tryout/blob/master/src/Conversations/Commands/CreateNewConversationCommandHandler.cs&quot; target=&quot;_blank&quot;&gt;https://github.com/wayne-o/ges-tryout/blob/master/src/Conversations/Commands/CreateNewConversationCommandHandler.cs&lt;/a&gt;&amp;nbsp;you can see how easy it is to save an aggregate root into the event store using the &lt;a href=&quot;https://github.com/wayne-o/ges-tryout/tree/master/lib/getting-started-with-event-store/src/GetEventStoreRepository&quot; target=&quot;_blank&quot;&gt;GetEventStoreRepository&lt;/a&gt;. This was one area that the developer is left to figure out - finding this repository was a great help.&lt;br /&gt;
&lt;br /&gt;
You can see in the AR that when I create a new conversation it fires a new ConversationStarted event. This event is listened to in the read model. The wiring up for the read model you can see here: &lt;a href=&quot;https://github.com/wayne-o/ges-tryout/blob/master/src/Ges.ReadModel/Program.cs&quot; target=&quot;_blank&quot;&gt;https://github.com/wayne-o/ges-tryout/blob/master/src/Ges.ReadModel/Program.cs&lt;/a&gt;. Again - there is some work to do here to turn the received events into something that can be worked with. There is also some (overly simple) code here to dispatch the events to the event handlers.&lt;br /&gt;
&lt;br /&gt;
Inside the event handlers I am denormalizing the data to a RavenDb read model.&lt;br /&gt;
&lt;br /&gt;
Aside from the added work of figuring out the event dispatcher I find the GES architecture much much simpler than the Azure based CQRS Journey code. There seems to be much less ceremony - even after I reduced it drastically in my refactoring of CQRS Journey. I haven&#39;t got any perf details for either implementation but Greg claims GES will natively handle 20,000 messages a second on a single node so I doubt perf is an issue for either implementation.&lt;br /&gt;
&lt;br /&gt;
Next up I&#39;ll be adding some real features using each of these infrastructures. I like to work from an emergent design BDD style approach so will be looking at which of these works best under a test 1st dev cycle.</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/3591054308602095404/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2014/07/sonatribe-conversations-platform-cqrs_30.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/3591054308602095404'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/3591054308602095404'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2014/07/sonatribe-conversations-platform-cqrs_30.html' title='Sonatribe conversations platform - CQRS knockout - part 2: GetEventStore.com'/><author><name>wayne</name><uri>http://www.blogger.com/profile/00603417641518780876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-2172783478279904162</id><published>2014-07-28T08:47:00.001-07:00</published><updated>2014-07-28T08:48:24.705-07:00</updated><title type='text'>Sonatribe conversations platform - CQRS knockout - part 1: CQRS Journey</title><content type='html'>I&#39;m about to start hacking away at a new part of the sonatribe API - a feature we&#39;ve been calling &lt;a href=&quot;http://blog.sonatribe.com/2014/07/next-big-thing-uncivilised-civilised-discussion/&quot;&gt;Conversations&lt;/a&gt;. I&#39;ve been a fan of CQRS/ES for a while now and think that the natural way that a conversation is a stream makes it a very good fit for some event store goodness.

&lt;br /&gt;
&lt;br /&gt;
Where as I used JOlivers EventStore in the past itteration of sonatribe (we attempted to make the whole site using CQRS/ES at one point but struggled with infrastructure and complexity) - things have moved on since then. Most notably Microsoft have done a whole Patterns &amp;amp; Practices feature on CQRS/ES and there is now code available that can be used in projects like this. And perhaps more exciting is the fact that GetEventStore.com is out in the wild and ready for use!&lt;br /&gt;
&lt;br /&gt;
Before I go running in and developing the platform with one or the other I decided to take them both for a spin. Starting with the MS P&amp;amp;P Cqrs Journey code I made a pretend set up so I could asses which would be better suited to our needs.&lt;br /&gt;
&lt;br /&gt;
I guess the main concerns for me are:&lt;br /&gt;
&lt;span id=&quot;goog_1338697404&quot;&gt;&lt;/span&gt;&lt;span id=&quot;goog_1338697405&quot;&gt;&lt;/span&gt;&lt;a href=&quot;https://www.blogger.com/&quot;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Simple dev cycle- as I mentioned above - complexity seemed to be a major time burner for us before - I want this implementation to be drastically simpler.&lt;/li&gt;
&lt;li&gt;Testable - this is a blatant must - previously this was quite hard - there was very little guidance here also.&lt;/li&gt;
&lt;li&gt;It needs to be able to run on my laptop on a train! I do a lot of this work during my commute!&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
So - taking the CQRS Journey code (&lt;a href=&quot;http://msdn.microsoft.com/en-us/library/jj554200.aspx&quot; target=&quot;_blank&quot;&gt;http://msdn.microsoft.com/en-us/library/jj554200.aspx&lt;/a&gt;) I hacked away and took out the bits I didn&#39;t like, the bits I didn&#39;t need and added some bits I felt I wanted. Mainly I don&#39;t like Unity IOC - I feel like you have to hold it&#39;s hand far too much. I initially tried swapping in Funq but it was too simple in features so ended up with Castle Windsor - my old time favourite but might be a bit bloaty for some. &amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
I only managed to port over the SQL bits - the Azure ServiceBus part is not working yet and probably won&#39;t - for reasons I&#39;ll specify in my next post.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
I also decided to use RavenDb as my read model - Table Storage is OK but RavenDb is absolutely nuts in terms of speed and simplicity.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Anyways - the code is here:&amp;nbsp;&lt;a href=&quot;https://github.com/wayne-o/cqrs-journey-tryout&quot; target=&quot;_blank&quot;&gt;https://github.com/wayne-o/cqrs-journey-tryout&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
My opinion after all this is: It&#39;s nice to have all the code - esp for the event store, the publishers and recievers etc. There is a _lot_ of config and messing about with the original code - I tried to simplify this by registering the command and event handlers by convention but it still feels very &quot;ceremony&quot;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Next up I&#39;ll be taking geteventstore.com for a spin - a very quick and simple dive into getting a command through an API and eventually handling the command to maintain a read model &amp;nbsp;- exactly the same scenario as the CQRS Journey try out - just using geteventstore.com as my event store...&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/2172783478279904162/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2014/07/sonatribe-conversations-platform-cqrs.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/2172783478279904162'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/2172783478279904162'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2014/07/sonatribe-conversations-platform-cqrs.html' title='Sonatribe conversations platform - CQRS knockout - part 1: CQRS Journey'/><author><name>wayne</name><uri>http://www.blogger.com/profile/00603417641518780876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-1004231531023240989</id><published>2014-07-19T07:27:00.001-07:00</published><updated>2014-07-19T07:29:15.720-07:00</updated><title type='text'>Stop Checking in the Settings!!</title><content type='html'>Ideally configuration hangs off of the build setting or (better still) the deployment process takes care of the configuration. If you&#39;re using Puppet or Octopus to deploy your code then this is super easy. Having this sort of infrastructure isn&#39;t always feasible especially on smaller projects with smaller teams. The project I&#39;m working on at the moment is one such codebase. We share settings.xml files, there are many of them and each developer needs to configure these files as per their own environment as well as the deployed environments - per feature. This is a problem we are very aware of and we have scheduled tickets to take care of it but in the mean time I&#39;m going to look into how we can use Git to limit the pain of this.&lt;br /&gt;
&lt;br /&gt;
I am a massive culprit for checking in settings files - everybody does it - especially the nub on the team (me)... It&#39;s really annoying to the poor person who does an update after the point and then has to rework the settings files. So here are a couple of ways we can fix this (this is very basic git stuff so if you&#39;re well versed on Git you&#39;ll be well aware of the following!):&lt;br /&gt;
&lt;br /&gt;
Open up a command prompt and navigate to where you keep your git repos. Mine are normally in d:\git&lt;br /&gt;
&lt;br /&gt;
Lets start with a fresh plate, run the following:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/95d0779cbd1e63fc0095.js&quot;&gt;&lt;/script&gt;

We&#39;ve just created a brand new git repo for us to play with. very straight forward. We don&#39;t want to work in master so lets create a new branch:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/ceaa39efd0bc65f0c16a.js&quot;&gt;&lt;/script&gt;&lt;br /&gt;
Now that we have the dev branch we can make our settings file.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/2fff437c4b6a6ec9d3a0.js&quot;&gt;&lt;/script&gt;

Add some dummy dev environment config to the settings.xml file. Now we can add that to the dev branch and commit it.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/6d2ea5b5975a693a7810.js&quot;&gt;&lt;/script&gt;

Now that that&#39;s safe and sound we can create our feature branch where we will want to do some work and manage our own config:&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/06453fee97fad73ff389.js&quot;&gt;&lt;/script&gt;

Put some code in test.cs so we can commit that into the wayne branch&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/a5076d2c60eb6a3e4fc5.js&quot;&gt;&lt;/script&gt;

Now lets customize the settings.xml file:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/dca34eed93e9484b6253.js&quot;&gt;&lt;/script&gt;

Make the dev specific settings feature specific so you know if this works or not when we come to doing the switcheroo. Now we want to commit the feature specific settings into the feature branch:
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/8bb8215fa8022ef04eeb.js&quot;&gt;&lt;/script&gt;

Now we&#39;ll continue to do some more work:
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/5ae223ffdfc412861d22.js&quot;&gt;&lt;/script&gt;

And commit that into the feature branch
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/82d84ff9059b2264712f.js&quot;&gt;&lt;/script&gt;

OK so now we have a dev branch with some settings in, we branched from dev to create a feature branch, did some work in that branch, configured the settings file to make it specific to that branch and then did even more work, big day in the office. What we want to do now is to merge the work in the feature branch to the dev branch - but we definitely do not want the settings from the feature branch to show in dev, that&#39;s just going to wind everyone up. &lt;br /&gt;
To do this we can revert the commit we made to push the settings into the git branch. We can check the log to see the commit and revert it. To do this run the following:
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/9ac8ca90368a89b79464.js&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
This will yield something like the following (type q to exit the log):&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/0da9a1c69c955ebf4ad2.js&quot;&gt;&lt;/script&gt;

As you can see the commit with ID &quot;965f41809225abbe2ed2b74d6e162e27c9c18a72&quot; so to revert this commit we can issue the following command:&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/465a56645f76302a0460.js&quot;&gt;&lt;/script&gt;

And then we can safely merge our feature branch into dev:
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/7e03af57f178c09cec6f.js&quot;&gt;&lt;/script&gt;

Open up settings.xml and verify it&#39;s as expected - dev settings, not feature settings. 
&lt;br /&gt;
So that was one way of making sure we don&#39;t merge stuff we don&#39;t want to merge. If we wanted to do some more work in the feature branch and get the settings back we can use the same approach above to undo the commit made by git revert - git simply creates a compensating action to revert the commit by and this creates another commit - so we can just revert the commit created by the compensating action (which will create another compensating - git is just event sourcing after all!). Another approach is cherry pick commits from the feature branch. This will apply a single commit to the branch you are merging into. Here is the general gist of it:
&lt;br /&gt;&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/11f69765c8d1b36d3b0a.js&quot;&gt;&lt;/script&gt;
&lt;br /&gt;
Git is powerful stuff - cherry picking is basically the converse to the 1st approach. It shows how useful doing small, discreet commits can be. In general there are only a small handful of git commands that I use frequently - you can do so much with a small amount of git commands it&#39;s ridiculous. But there are some extremely useful, more &quot;advanced&quot; features which really bring Git into it&#39;s own. I&#39;m still learning Git but one thing I have found is that using it properly really pays off and there is always a sane solution to whatever insane mess I find myself in!





</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/1004231531023240989/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2014/07/stop-checking-in-settings.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/1004231531023240989'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/1004231531023240989'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2014/07/stop-checking-in-settings.html' title='Stop Checking in the Settings!!'/><author><name>wayne</name><uri>http://www.blogger.com/profile/00603417641518780876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-6513829582039522675</id><published>2014-05-25T06:15:00.001-07:00</published><updated>2014-05-25T06:15:26.854-07:00</updated><title type='text'>Regression testing ServiceStack services with RavenDB embeded</title><content type='html'>One of the great things about using RavenDB and ServiceStack on Sonatribe is the ability to spin the whole system up inside a unit test. ServiceStack offers&amp;nbsp;AppHostHttpListenerBase which can run in process and RavenDB offers the embeded database which can run in-memory. The fact that these two run in-process and in-memory means that regression testing Sonatribe REST services is simple and fast. Almost as fast as unit testing!&lt;br /&gt;
&lt;br /&gt;
To be able to run tests like the following regression test:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/0b4565fcb06f7ac0351b.js&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
&lt;br /&gt;
I use the AppHostHttpListenerBase in my BaseTest class. Which is roughly the same as the following:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/6e00fdc43a3a204a8bba.js&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
Now, When I inherit from SelfHostedBaseTest each test spins up a new host and a new in-memory RavenDB instance. This means I have a clean slate for each test, ideal for running everything in isolation. Not only this but I am also testing my services in the exact same stack as they run in production!</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/6513829582039522675/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2014/05/regression-testing-servicestack.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/6513829582039522675'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/6513829582039522675'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2014/05/regression-testing-servicestack.html' title='Regression testing ServiceStack services with RavenDB embeded'/><author><name>wayne</name><uri>http://www.blogger.com/profile/00603417641518780876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-2074608584424221213</id><published>2014-05-24T09:37:00.002-07:00</published><updated>2014-05-24T09:37:47.714-07:00</updated><title type='text'>CQRS without CQRS and RavenDB</title><content type='html'>Following on from my previous post about sonatribe, one of the hard decisions I had to make on this iteration was refactoring out the CQRS code &amp;nbsp;to simplify the dev cycle and improve velocity. I&#39;m a big proponent of CQRS - it answers a lot of questions in traditional dev and while there is some upfront complexity it is far outweighed by the lack of accidental complexity apparent in traditional software projects.&lt;br /&gt;
&lt;br /&gt;
One of my favorite features of a CQRS system is the denomalized rad layer - where the data stored in the read store maps directly to the DTO&#39;s coming out of the UI. When I refactored the code, I really wanted to be able to still get this sort of functionality. Denormalized read models have been around a long long time - SQL Server has &quot;Views&quot; where the developer can specify some SQL - normally making use of JOIN and UNION to present a readily available denormalized view over the data.&lt;br /&gt;
&lt;br /&gt;
In RavenDb we can achieve this using TransformResults. In a document database you model your data differently - rather than joins and lookup tables you can store reference data inside the main document (aggregate root). A good rule of thumb being that if you ever need to load that part of the data alone, independent of other data, then that data is a document of it&#39;s own, with it&#39;s own ID. Documents can reference other documents however using the Denormalized Reference pattern (http://ravendb.net/docs/2.5/faq/denormalized-references). Denormalized references can become a chore when the referenced data gets updated - in this case you will need to PATCH the documents to reflect any changes needed.&lt;br /&gt;
&lt;br /&gt;
One of the cases where we have used TransformResults is to present the event profile (a festivals main page on the website) - in order to cut down requests to gather the data to present on this view (tags, images, users who are going, campsites, lineup info etc) we can aggregate all of that information in one TransformResult:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/24db432bf9de1b5c2d24.js&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
While this looks quite the beast - it&#39;s actually quite simple - we&#39;re pre-compiling all of the associated data for each event. This means we can pull all of the JSON needed for an event profile using a single call!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/3c1449eedbd60baca8ef.js&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
It&#39;s ridiculously simple and without cache turned on we can return from the REST API an event profile in less than 100 milliseconds - with cache turned on and tuned we can smash that right down - that kind of optimization comes much later however.&lt;br /&gt;
&lt;br /&gt;
Now this isn&#39;t _really_ anything like CQRS - but it gives me a good-enough-for-now alternative. At some point I will be bringing CQRS back into sonatribe, this time round most likely using Greg Youngs GetEventStore (http://geteventstore.com/) - previously I used JOlivers EventStore and CommonDomain (which is used in Greg Young&#39;s implementation to my understanding). But for now I&#39;m happy with this setup and it&#39;s simplicity.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/2074608584424221213/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2014/05/cqrs-without-cqrs-and-ravendb.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/2074608584424221213'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/2074608584424221213'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2014/05/cqrs-without-cqrs-and-ravendb.html' title='CQRS without CQRS and RavenDB'/><author><name>wayne</name><uri>http://www.blogger.com/profile/00603417641518780876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-950956760172244688</id><published>2014-05-24T09:10:00.000-07:00</published><updated>2014-05-24T09:10:11.709-07:00</updated><title type='text'>Sonatribe technical bits and pieces</title><content type='html'>The sonatribe project makes use of a broad range of technology from PHP to Microsoft Azure and a lot in between. We host the main site on Ubuntu but the backend is hosted on Azure and makes use of ServiceStack to build the REST API. The main components of this project are:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;PHP UI - running on an Azure Ubuntu VM&lt;/li&gt;
&lt;li&gt;ServiceStack REST API - running on an Azure Windows VM&lt;/li&gt;
&lt;li&gt;RavenDB database&lt;/li&gt;
&lt;li&gt;EXTJS admin application&lt;/li&gt;
&lt;li&gt;2 x Azure worker roles used to process event and artist information&lt;/li&gt;
&lt;li&gt;Azure ServiceBus&lt;/li&gt;
&lt;li&gt;Azure Cache&lt;/li&gt;
&lt;li&gt;Azure CDN&lt;/li&gt;
&lt;li&gt;SignalR backed chat server&lt;/li&gt;
&lt;li&gt;Xamarin iOS app&lt;/li&gt;
&lt;li&gt;Xamarin Android app&lt;/li&gt;
&lt;li&gt;Facebook canvas&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
Sonatribe has always been developed in our (stu, chris and myself) spare time - so no wonder it&#39;s taken ~3 years to develop and get the alpha out the door! The initial build of sonatribe (the site is currently on it&#39;s third rebuild!) used a CQRS backend and although I maintain this is the best architecture for a project like sonatribe, the complexity in the development/test cycle slowed progress too much, for such a small team trying to get a working &quot;something&quot; out the door, I had to make the hard decision to refactor the code and simplify it.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Both the main website &amp;nbsp;(PHP UI) and the Android &amp;amp; iOS apps use the same API for authentication and data. This is a great design as it means all of the business logic stays in one place. And when you&#39;re building a system that relies on multi-axis permissions/roles with a range of business logic and views over the data - this is a big advantage.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The Extjs admin app allows us to import data from clashfinder. The data coming from that JSON is very basic and only specifies the act name, stage and time of the set. In order to build upon that data to be able to provide spotify playlists, artist bio&#39;s and images etc we have an import pipeline which also allows us to update that data (quite complex as the clashfinder data has no Id information). Importing information for each act is quite a long running process as we gather as much information as we can about each artist, we download images relevant to each artist and generate renditions for use in the site. Because of this long running process the import process follows the following flow (ignoring clashfinder updates - that&#39;s another story!):&lt;/div&gt;
&lt;div&gt;
&lt;ul&gt;
&lt;li&gt;Initial import fired from admin app&lt;/li&gt;
&lt;li&gt;The REST service picks up the import ID and pulls the data from clashfinder&lt;/li&gt;
&lt;li&gt;The JSON is deserialized into POCO&lt;/li&gt;
&lt;li&gt;A RavenDb bulk insert operation is started&lt;/li&gt;
&lt;li&gt;The locations (stages) are pulled out of the data and are added to our database&lt;/li&gt;
&lt;li&gt;For each of the artist sets we create a new&amp;nbsp;listing event - a simple document which specifies the act name, stage, start and end&lt;/li&gt;
&lt;li&gt;The bulk insert is committed to the DB - this process can take ~ 1 sec for a large festival with ~2000 acts and ~190 stages&lt;/li&gt;
&lt;li&gt;For each listing event that was created we push a message onto the Azure ServiceBus&lt;/li&gt;
&lt;li&gt;The worker process at the end of this queue picks up each message and processes it asynchronously.&lt;/li&gt;
&lt;li&gt;We check to see if we can find any information about the act by using their name - we check spotify, last.fm, music brainz, wikipedia etc&lt;/li&gt;
&lt;li&gt;If we can find the artist we download their spotify track Ids, artist bio, images etc and create a new artist in our DB linking all of these assets.&lt;/li&gt;
&lt;li&gt;The artist is added to the listing event and saved&lt;/li&gt;
&lt;li&gt;If we can&#39;t find the artist by the act name alone we try to break the name down - searching for substrings - perhaps the act is a collaboration.&lt;/li&gt;
&lt;li&gt;For each artist we find in the act name we add them to the listing event and save it away&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
The admin site has it&#39;s own schedule designer using&amp;nbsp;bryntum scheduler which we can use to create and edit lineups. The preferred method atm is clashfinder imports due to the communal collaboration that goes into creating them.&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Using Xamarin to develop the Android and iOS apps means we can share a lot of the code base between the apps as well as make use of SignalR to provide chat functionality. Servicestack also provides a great PCL client - it&#39;s a no brainer. The apps are currently in the early stages of development - at the moment you can log in, view festivals and their lineups. We aim to have very basic implementations out in the app stores before Glasto. The apps will be free :)&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
We&#39;ve recently released the 1st alpha of sonatribe (http://alpha.sonatribe.com) and while there are a few rough edges we&#39;re very pleased with the result. The platform differs from anything out there at the moment because of the social aspect of the site as well as the fact that we will be offering native Android and iOS apps. While the alpha is a very bare bones (and in some places quite clumsy) implementation, we have a great platform to build on. This year our aim is to stabilize the platform, introduce the mobile apps and react to feedback. Our next big undertaking is tackling conversations, to build on the social aspect of the site. We&#39;re planning on swerving the traditional &quot;forum software&quot; implementation and intend to build a conversation platform from the ground up based on the usage of our demographic - something we can grow and improve upon and will be suitable for the next 10 years - not extinct 10 years ago. We&#39;ve always found forums to be too 1990&#39;s and we&#39;ve always said Facebook waters down the semantics too much for something like this (the whole reason sonatribe exists).&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
We&#39;ve got a great set of features and a lot of work has gone into getting to where we are, we&#39;re listening to user feedback and looking forward to the next set of great features. There&#39;s a hell of a lot left to do though!&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
w://&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/950956760172244688/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2014/05/sonatribe-technical-bits-and-pieces.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/950956760172244688'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/950956760172244688'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2014/05/sonatribe-technical-bits-and-pieces.html' title='Sonatribe technical bits and pieces'/><author><name>wayne</name><uri>http://www.blogger.com/profile/00603417641518780876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-6070793125072312159</id><published>2014-04-03T00:19:00.000-07:00</published><updated>2014-04-03T00:19:43.043-07:00</updated><title type='text'>Sonatribe Alpha</title><content type='html'>Alpha = release to friends and family!&lt;br /&gt;
&lt;br /&gt;
Sonatribe has
 been a long time coming! We started the project as FestivalStar around 
2009 in a kind of accidental, off the cuff development. Back then it was
 an iPhone app that supported multiple festivals which did quite well on
 the AppStore. Since then we&#39;ve scrapped the iPhone app (for now) had a 
change of direction, a complete redesign and after about 4 iterations 
(and 5 years!) later we&#39;re about ready for an alpha release! 

Sonatribe is built by Chris, Stu and myself. We&#39;ve been hacking away at 
this in our spare time so it&#39;s been a long and windy road.&lt;br /&gt;
&lt;br /&gt;
The
 alpha that we&#39;re releasing is by no means the end product. We&#39;ve got 
the project to a state where we have a small, manageable set of features
 we hope will be useful to festival goers. Sonatribe development is a 
constant ongoing thing and we&#39;d like to generate a feedback loop from 
our users where by we shape the site and it&#39;s features based on this 
feedback loop. 

At the moment we have the skeleton to be able to provide you a small set
 of tools to socialize and plan your festival experience. By using the 
site we can get an idea of your likes in terms of music genres and can 
provide you recommendations for bands/DJs and events you might not have 
heard of before.&lt;br /&gt;
&lt;br /&gt;
We&#39;ve teamed up with clashfinder.com 
to provide lineup data (we have our own lineup designer in the backend 
too which we feed back to clashfinder). We&#39;ve also integrated with 
Last.fm to provide artist information.&lt;br /&gt;
&lt;br /&gt;
So what do you 
get? Like I said, it&#39;s early days but at the moment you can browse 
festivals, see the lineups for the festivals, create your own schedule, 
view act information (where they&#39;ve played, where they&#39;re playing, bio, 
images etc), create a &quot;campsite&quot; and invite friends to it. When you have
 a campsite you get a private place to chat and a group schedule. You 
also get a (at the moment - very simple) profile page like any social 
network with private messaging.&lt;br /&gt;
&lt;br /&gt;
We&#39;ve built Sonatribe 
from scratch to be exactly what it is - a social network for festival 
lovers. We&#39;ve not shoehorned any blogging platform to create it. We have
 complete flexibility in it&#39;s future direction and a metric ton of ideas
 for features and goodies. There&#39;s no advertising and we have no 
intention of splattering the site with any. We&#39;re not sure how the site 
will pay for itself as yet but we want to keep it as clean and user 
friendly as possible.&lt;br /&gt;
&lt;br /&gt;
At this stage sonatribe.com is an
 alpha release and so you shouldn&#39;t expect it to be a complete and 
finished product. There will be some workflows that could be better, 
some of the site may seem a little slower than we&#39;d like and somethings 
might just fail! There might be some features you&#39;d like to see and some
 you&#39;d like to tweak. All of your feedback is valuable and will help 
shape the project as it matures.&lt;br /&gt;
&lt;br /&gt;
How to provide 
feedback: On the right hand side of the site there is a tab you can 
click to provide feedback. If you want to be notified of progress via 
email you can join the sonatribe-users email list: &lt;a href=&quot;https://groups.google.com/forum/#!forum/sonatribe-users%20&quot; target=&quot;_blank&quot;&gt;https://groups.google.com/forum/#!forum/sonatribe-users &lt;/a&gt;&lt;br /&gt;
How to access the site: You can get to the site at: [You&#39;ll be 
provided the super secret URL!] - you will need a Twitter, Google or 
Facebook account to login.&lt;br /&gt;
&lt;br /&gt;
Rules of the road:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;You can invite some friends, we&#39;d like to get some real world usage.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;We might have to cap new members at some point while we work out the
 inevitable creases but for now we&#39;d just like to get the site working 
through it&#39;s paces.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;If you&#39;re going to a festival that isn&#39;t listed just pop us an email, get in touch using the &quot;Feedback and support&quot; tab. &lt;/li&gt;
&lt;li&gt;This is an alpha, things might go wrong for you, if anything dodgy 
happens please let us know and we&#39;ll be able to make the site more 
stable using your feedback.&lt;/li&gt;
&lt;/ul&gt;
And finally, we aren&#39;t affiliated with anyone, nor do we claim 
to. If you&#39;re a festival organizer we&#39;d love to hear from you. Our 
intention is to provide a means for users to discover your events and 
help build your community. We&#39;re building tools to help you manage your 
events and interact with your community which we aim to provide for 
free.&lt;br /&gt;
&lt;br /&gt;
Looking forward to receiving your feedback!&lt;br /&gt;
&lt;br /&gt;
Wayne, Chris, Stu

</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/6070793125072312159/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2014/03/sonatribe-alpha.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/6070793125072312159'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/6070793125072312159'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2014/03/sonatribe-alpha.html' title='Sonatribe Alpha'/><author><name>wayne</name><uri>http://www.blogger.com/profile/00603417641518780876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-5932540965708831843</id><published>2013-10-28T08:58:00.002-07:00</published><updated>2013-10-28T08:58:55.784-07:00</updated><title type='text'>Xamarin  Android 3G helper</title><content type='html'>Managing 3G connectivity in Xamarin Android is a nause - you have to mess about with JNI and a host of other gruff. I&#39;ve created this little helper to make it easy:
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/7199431.js&quot;&gt;&lt;/script&gt;</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/5932540965708831843/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2013/10/managing-3g-connectivity-in-xamarin.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/5932540965708831843'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/5932540965708831843'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2013/10/managing-3g-connectivity-in-xamarin.html' title='Xamarin  Android 3G helper'/><author><name>wayne</name><uri>http://www.blogger.com/profile/00603417641518780876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-5603100972761328952</id><published>2013-10-28T03:33:00.000-07:00</published><updated>2013-10-28T04:03:05.993-07:00</updated><title type='text'>RavenDb tip – deploying unstable builds regularly [REPOST]</title><content type='html'>When significant updated builds to RavenDb are posted to the Hibernating Rhinos build page (which is fairly frequently – a good thing) it means I need to deploy the updated DLLs to the servers. We currenty manage 8 instances of RavenDb – 1 for each service that supports sonatribe. That can be an aruous task – especially as we host 4 isntances on 1 server and 4 on another.&lt;br /&gt;
&lt;br /&gt;
I’ve been playing about with PowerShell recently and thought this would be a good opportunity to get my teeth into it. I was initially trying to get the WebDeploy stuff to work scripted but that was a massive nause.&lt;br /&gt;
&lt;br /&gt;
I ended up just going with a script i can run on each server. Here are the steps I needed to accomplish:&lt;br /&gt;
&lt;br /&gt;
stop iis&lt;br /&gt;
remove the old files from the db folder including the bin and all it’s contents but excluding the Data directory and the web.config and log4net.config files&lt;br /&gt;
copy the contents of the deplyment folder (which i set up to include the plugins needed) to the database directory&lt;br /&gt;
restart iis&lt;br /&gt;
Simples.&lt;br /&gt;
&lt;br /&gt;
Here’s the script:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/7194622.js&quot;&gt;&lt;/script&gt;
&lt;br /&gt;
Nice and simple. Just create an array of strings containing the paths to the folders where ravendb is hosted. Loop through them deleting and copying. Restart IIS.&lt;br /&gt;
&lt;br /&gt;
This saves me about an hour a week and also guarantees that I don’t accidentally miss something. It’s a shame the web-deploy stuff in msdeploy didn’t work – we host the databases in IIS so it would have been a suitable solution and would have meant i could deploy from my dev machine without even needing to log onto the server. I expect I’ll try and tackle that at some other point.&lt;br /&gt;
&lt;br /&gt;
w://</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/5603100972761328952/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2013/10/ravendb-tip-deploying-unstable-builds.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/5603100972761328952'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/5603100972761328952'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2013/10/ravendb-tip-deploying-unstable-builds.html' title='RavenDb tip – deploying unstable builds regularly [REPOST]'/><author><name>wayne</name><uri>http://www.blogger.com/profile/00603417641518780876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-4106408641777977336</id><published>2013-10-28T03:29:00.002-07:00</published><updated>2013-10-28T04:03:13.281-07:00</updated><title type='text'>Taking a stab at generating strongly typed Facebook Graph C# classes for .Net/Asp.Net (My attempt)  [REPOST]</title><content type='html'>The facebook API is flakey to work with at the best of times. However as Deap mentioned on his blog post: facebook documentation requires login (showstopper) it’s gotten even worse if you want to automate the process by scraping the docs.

I’ve managed to take a stab at this using a different method to retrieve the docs – this method allows us to 1st login to the FB docs and then scrape away – building up the strongly typed objects on the way.

Here’s my branch: FacebookSdk-contrib on github. This is using HtmlUnit – a Java project – so we’re also using a wrapper to use it under the IKVM runtime.

It’s nearly there. Just some final bits to iron out.

w:/</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/4106408641777977336/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2013/10/taking-stab-at-generating-strongly.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/4106408641777977336'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/4106408641777977336'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2013/10/taking-stab-at-generating-strongly.html' title='Taking a stab at generating strongly typed Facebook Graph C# classes for .Net/Asp.Net (My attempt)  [REPOST]'/><author><name>wayne</name><uri>http://www.blogger.com/profile/00603417641518780876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-9211370840204005116</id><published>2013-10-28T03:28:00.001-07:00</published><updated>2013-10-28T04:03:21.693-07:00</updated><title type='text'>Replication predicates in RavenDb  [REPOST]</title><content type='html'>Replication in RavenDb is pretty fekin sweet. I helped Oren by submitting bugs and testing it out in sonatribe dev by pushing it in a real world usage scenario. One of the features I asked about was the ability to filter out parts of the namespace for replication – instead of the ‘all or nothing’ approach used by default. Oren (as you’d expect) simply replied with a request for a patch. So here is my patch.&lt;br /&gt;

&lt;script src=&quot;https://gist.github.com/wayne-o/7194570.js&quot;&gt;&lt;/script&gt;

&lt;br /&gt;&lt;br /&gt;
My fork is here:&amp;nbsp;&lt;a href=&quot;http://web.archive.org/web/20110613160916/https://github.com/wayne-o/ravendb&quot;&gt;https://github.com/wayne-o/ravendb&lt;/a&gt;&lt;br /&gt;
Basically – my aim is to allow the developer to set replication filters – so only a part of a namespace is replicated – rather than the whole db. To do this I’ve added a ReplicationPredicate class in the Raven.Bundles.Replication.Data namespace. this is used when initialising the db:&lt;br /&gt;
&lt;pre class=&quot;brush: csharp; title: ;&quot; title=&quot;&quot;&gt;&lt;/pre&gt;
it’s a work in progress but i’m finding it hard to get the time to commit to it due to the new baby. if anyone fancies lending a hand – there’s a test in the SimpleReplication.cs file Can_filter_replication_using_replication_predicates&lt;br /&gt;
w://</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/9211370840204005116/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2013/10/replication-in-ravendb-is-pretty-fekin.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/9211370840204005116'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/9211370840204005116'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2013/10/replication-in-ravendb-is-pretty-fekin.html' title='Replication predicates in RavenDb  [REPOST]'/><author><name>wayne</name><uri>http://www.blogger.com/profile/00603417641518780876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-6129273120349837300</id><published>2013-10-28T03:25:00.000-07:00</published><updated>2014-10-16T06:54:36.949-07:00</updated><title type='text'>Asp.Net Mvc action selector – with list support  [REPOST]</title><content type='html'>Here is my updated AcceptParameterAttribute class with support for data bound lists:&lt;br /&gt;
&lt;br /&gt;

&lt;script src=&quot;https://gist.github.com/wayne-o/7194520.js&quot;&gt;&lt;/script&gt;

some sample code to use this updated version:&lt;br /&gt;
&lt;br /&gt;
decorate the action with something like:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/7194525.js&quot;&gt;&lt;/script&gt;


&lt;br /&gt;
and then the buttons in the list can have the following format for the name:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/7194532.js&quot;&gt;&lt;/script&gt;

hth</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/6129273120349837300/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2013/10/aspnet-mvc-action-selector-with-list.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/6129273120349837300'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/6129273120349837300'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2013/10/aspnet-mvc-action-selector-with-list.html' title='Asp.Net Mvc action selector – with list support  [REPOST]'/><author><name>wayne</name><uri>http://www.blogger.com/profile/00603417641518780876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-6277310746661443046</id><published>2013-10-24T09:24:00.002-07:00</published><updated>2013-10-24T09:25:31.257-07:00</updated><title type='text'>Installing Solr in Apache on Windows</title><content type='html'>I&#39;ve been messing about with Solr a lot recently and although the finished setup is great at what it does, getting it setup in the first place is a complete nause - especially on Windows. Anything to do with Java is going to be an XML configuration nightmare so to help myself in the future and whoever else stumbles into the same issues I had, I&#39;ll document the process here.&lt;br /&gt;
&lt;br /&gt;
First up you want to ensure you have the latest install of the Java SDK installed. Grab that from Oracle and do a standard install.&amp;nbsp;http://www.oracle.com/technetwork/java/javase/downloads/index.html&lt;br /&gt;
&lt;br /&gt;
Next up you want to get Apache installed as a Windows Service. I installed the latest to date (Tomcat 8.0.0-RC5 (alpha)) - grab that from here:&amp;nbsp;&lt;a href=&quot;http://tomcat.apache.org/download-80.cgi&quot;&gt;http://tomcat.apache.org/download-80.cgi&lt;/a&gt;&amp;nbsp;and follow the instructions.&lt;br /&gt;
&lt;br /&gt;
I&#39;m not going to go into the details of installing the packages above as they are standard installations and you should accept the defaults in most cases.&lt;br /&gt;
&lt;br /&gt;
Now that we have Tomcat installed and running we can use it as the container to host the Solr instance. Grab the latest build of Solr from their website (http://lucene.apache.org/solr/). &amp;nbsp;This is where the install gets a little interesting. Getting Solr running for your requirements will mean altering a lot of config XML files. It&#39;s obviously very handy to have those XML files under source control so keeping the config files inside the default locations withing Solr is not going to work. In this setup we&#39;ll want to point Solr to a home directory other than the default so we can keep the config files under Gits control.&lt;br /&gt;
&lt;br /&gt;
Once Solr has downloaded unzip it and grab Solr***.war (where *** is the version number) file from the solr-4.5.0\solr-4.5.0\dist folder and place it inside the lib (C:\Program Files\Apache Software Foundation\Tomcat 8.0\lib) folder in Apache - rename it so that it is called Solr.war. Now grab all of the jar files from&amp;nbsp;solr-4.5.0\solr-4.5.0\dist\solrj-lib and&amp;nbsp;solr-4.5.0\solr-4.5.0\dist folders and place them inside the same lib folder in Apache. It might not be necessary to install all of the jar files but incrementally adding them by deciphering Solrs exceptions takes an age and I doubt it has any negative impact.&lt;br /&gt;
&lt;br /&gt;
As I mentioned before, we want to store the config outside of the normal Solr installation so that it can live in the main repo folder along with whatever other supporting code you may have. To do this we need to tell Apache where to find our Solr home. To do this we need to place some XML inside Apache&#39;s conf directory (mine is at C:\Program Files\Apache Software Foundation\Tomcat 8.0\conf\Catalina\localhost). Here is an example which points Solr to c:\solr&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/7139589.js&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
Now restart the Apache service. If everything has gone well, navigating to http://localhost:8080 should bring up the Apache homepage. Navigating to&amp;nbsp;http://localhost:6161/solr/ will bring up &amp;nbsp;an error complaining about collection1 not being available. This is to be expected as we haven&#39;t given the core config yet.&lt;br /&gt;
&lt;br /&gt;
At this point, if you&#39;re seeing other isuses you&#39;re going to need to start using the logs directory in Apache to debug whatever issues you may have. If you have reached the same point as me then you can carry on the installation as follows.&lt;br /&gt;
&lt;br /&gt;
Inside the Solr download folder there is an example installation. Grab the solr folder from inside the examples: (\solr-4.5.0\solr-4.5.0\example\) and copy all of the files to c:\solr - or where ever you have decided your config home directory is. You&#39;ll need to open solr.xml and replace the contents with this:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/7139802.js&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
Rename the collection1 folder to test-solr as that is what is specified in the solr.xml. Restart Apache and reload http://loalhost:8080/solr. You should now see Solr&#39;s homepage (all be it with a warning like: Error Instantiating SearchComponent, solr.clustering.ClusteringComponent failed to instantiate)!!&lt;br /&gt;
&lt;br /&gt;
SearchComponent is a contrib to the Solr project. For this example I have no need to go into getting that working so I&#39;ll just remove it form the config. Edit the solrconfig.xml file to look like this:&lt;a href=&quot;https://gist.github.com/wayne-o/7140242&quot; target=&quot;_blank&quot;&gt; https://gist.github.com/wayne-o/7140242&lt;/a&gt; (linked not previewed as it&#39;s huge).&lt;br /&gt;
&lt;br /&gt;
Now restart Apache and refresh the solr homepage. You should now be able to select the new test-solr core in the UI!&lt;br /&gt;
&lt;br /&gt;
As a final note - Unless you want to keep the solr index (you don&#39;t) I would add the data folder to the gitignore file - or svn ignore it - whatever SCC you use.</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/6277310746661443046/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2013/10/installing-solr-in-apache-on-windows.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/6277310746661443046'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/6277310746661443046'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2013/10/installing-solr-in-apache-on-windows.html' title='Installing Solr in Apache on Windows'/><author><name>wayne</name><uri>http://www.blogger.com/profile/00603417641518780876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-8055566259261095021</id><published>2013-07-12T05:04:00.000-07:00</published><updated>2013-07-12T05:06:58.164-07:00</updated><title type='text'>Testing Android apps using Calabash</title><content type='html'>BDD testing ASP.NET Mvc websites is a well beaten path with the likes of Nunit, Specflow, Selenium WebDriver etc but testing the features of an iPhone or Android app with an automated UI test suite is not so well covered. Xamarin have recently bought out LessPainful (https://www.lesspainful.com/); the guys who make Calabash (http://calaba.sh/). Calabash is an open source project which aims to deliver BDD driven UI tests to the mobile device. LessPainful allows developers to run those tests in the cloud on a huge number of devices. Xamarin are taking LessPainful and adding a C# API (currently Calabash is driven through Ruby). Tests are written using the Cucumber DSL and custom steps are implemented using Ruby. With a C# API, developers will be able to define steps using Nunit which means SpecFlow could be used to add cucumber support and automatic step skeleton creation. Potentially sharing feature files across Android / iPhone as well as HTML 5 mobile app tests. &lt;br /&gt;
&lt;br /&gt;
For now though there is no public access to Xamarin&#39;s TestCloud (http://xamarin.com/test-cloud), the new name for LessPainful. The Calabash source is still open and available on GitHub, so anyone can set up and run BDD style tests using Calabash - just no TestCloud access for a while. &lt;br /&gt;
&lt;br /&gt;
At Psonar we wanted to get end to end coverage on UI features and though a lot of the code is covered by unit tests, there&#39;s nothing like getting feedback from automated UI tests. Setting up Calabash was not a straight forward &quot;install this, run this; BOOM&quot; type of process. So I&#39;ve written up my findings for future devs to get their heads around the process, it is a well worthy investment of time. I&#39;m no Ruby/Java dev. The exceptions and error reporting as well as versioning hell that I&#39;ve just been through hasn&#39;t persuaded me to jump ship yet either! Some understanding of the two are very helpful though.&lt;br /&gt;
&lt;br /&gt;
First of all you need to make sure you have Ant (http://ant.apache.org/) installed. I&#39;ve not had any problems installing this; it&#39;s very straightforward &quot;unzip to c:\ant, add ANT_HOME env var&quot;. Simples. 
&lt;br /&gt;
&lt;br /&gt;
Next up you want to download ruby 1.9 + devkit (version 1.9 is important as 2.0 has no compatible ghurkin lex parser - this tripped me up big time):
&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://dl.bintray.com/oneclick/rubyinstaller/rubyinstaller-1.9.3-p448.exe?direct&quot;&gt;http://dl.bintray.com/oneclick/rubyinstaller/rubyinstaller-1.9.3-p448.exe?direct&lt;/a&gt;
&lt;a href=&quot;https://github.com/downloads/oneclick/rubyinstaller/DevKit-tdm-32-4.5.2-20111229-1559-sfx.exe&quot;&gt;https://github.com/downloads/oneclick/rubyinstaller/DevKit-tdm-32-4.5.2-20111229-1559-sfx.exe&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Installing Ruby is simple, just double click and run. Installing the DevKit is similar to Ant in that you need to unzip it to c:\DevKit, add that path to your PATH env var. Open a cmd and navigate to c:\DevKit and run the following two commands:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/5983823.js&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
This configures the DevKit in your Ruby installations. Next up you need to use the following command to install the calabash-android gem:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/5983835.js&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
If you are getting errors at this point about not being able to compile native gems, you probably installed the wrong versions of Ruby and DevKit.&lt;br /&gt;
&lt;br /&gt;
All being well you should now be able to create a new folder for your Calabash tests and navigate to it and run:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/5983853.js&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
This will install a default skeleton Calabash directory structure with some example cucumber code. Adding your app to the directory involves (in visual studio):&lt;br /&gt;
Select Release build (Calabash only supports release build APK&#39;s atm). Ensure you have no SDK linking in the projects properties view. Select Tools/Deploy Android Project. You need to specify the Android debug key located in &quot;AppData\Local\Xamarin\Mono for Android&quot;. Specify the following when prompted:&lt;br /&gt;
&lt;br /&gt;
password: android&lt;br /&gt;
alias: androiddebugkey&lt;br /&gt;
key password: android&lt;br /&gt;
&lt;br /&gt;
Open a new cmd and run:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/5983899.js&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
This fires Android ADB - the debug bridge that effectively outs all of the banter Android has with itself; very handy for debugging problems.

Once the app is published and you&#39;ve followed these steps copy the APK to the Calabash test folder and in another cmd window run:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/wayne-o/5983892.js&quot;&gt;&lt;/script&gt;

This will tell calabash to copy the app to the device or emulator and the Calabash server (installed next to your app on the device) will start receiving delegated commands issued from the calabash process running on your computer (which interprets the Cucumber test scripts and turns them into commands sent to the device as JSON).

To get you started there are a bunch of pre-canned test steps on the Calabash GitHub Wiki:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;
&lt;a href=&quot;https://github.com/calabash/calabash-android/blob/master/ruby-gem/lib/calabash-android/canned_steps.md&quot;&gt;https://github.com/calabash/calabash-android/blob/master/ruby-gem/lib/calabash-android/canned_steps.md&lt;/a&gt;&amp;nbsp;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
My advice (if you are a Xamarin developer) would be to stick rigidly with the pre-canned steps for the moment. Stepping outside of this safety wall will mean you will more than likely face porting them over to C# at some point in the future. While I am sure Xamarin will maintain the ruby implementation, no one really wants 50/50 Ruby/C# definitions to maintain. The pre-canned steps seem to be fairly comprehensive and allow for a lot of expression so only advanced scenarios would absolutely require custom steps definitions. Although only using pre-canned steps are a lot more verbose (you can&#39;t aggregate steps for &quot;Given&quot; style steps for instance) - it&#39;s a price worth putting up with for now.   &lt;br /&gt;
&lt;br /&gt;
I will try and add a follow up to this post explaining the process of adding iPhone tests - sharing the feature files across both OS&#39;s. Very tasty indeed!&lt;br /&gt;
&lt;br /&gt;
w</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/8055566259261095021/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2013/07/testing-android-apps-using-calabash.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/8055566259261095021'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/8055566259261095021'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2013/07/testing-android-apps-using-calabash.html' title='Testing Android apps using Calabash'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-1045578399050849462</id><published>2012-10-30T14:22:00.002-07:00</published><updated>2012-10-30T14:34:54.566-07:00</updated><title type='text'>Building the www.brightonsausageco.com website - 30/10/2012</title><content type='html'>&lt;p&gt;This is a follow up to the 1st in this series: &lt;a href=&quot;http://iwayneo.blogspot.co.uk/2012/10/building-www.html&quot;&gt;http://iwayneo.blogspot.co.uk/2012/10/building-www.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the second &lt;a href=&quot;http://iwayneo.blogspot.co.uk/2012/10/building-www_16.html&quot;&gt;http://iwayneo.blogspot.co.uk/2012/10/building-www_16.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the third: &lt;a href=&quot;http://iwayneo.blogspot.co.uk/2012/10/building-www_26.html&quot;&gt;http://iwayneo.blogspot.co.uk/2012/10/building-www_26.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the fourth: &lt;a href=&quot;http://iwayneo.blogspot.co.uk/2012/10/building-wwwbrightonsausagecocom.html&quot;&gt;http://iwayneo.blogspot.co.uk/2012/10/building-wwwbrightonsausagecocom.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Battery died on the train to Nottingham yesterday so I shall be starting back on it tonight! Looking back at my final sentence before the battery karked it I see: &quot;Let&#39;s go and add the IRepository&lt;T&gt;.GetAll() method then&quot;... I best look at the tests and make sure that&#39;s where I left off...&lt;/p&gt;

&lt;p&gt;After having a ganders I can see that I have added a MongoInstaller and MongoCollectionComponentLoader (wholesale robbed from https://gist.github.com/2427676 barr a couple of minor mods) which allows me to inject a lazy loaded MongoCollection&lt;T&gt; into my repositories. My repository implementation now looks like:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/3982488.js?file=gistfile1.cs&quot;&gt;&lt;/script&gt;

&lt;p&gt;And a test:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/3982502.js?file=gistfile1.cs&quot;&gt;&lt;/script&gt;

&lt;p&gt;So, now we have the repository working with MongoDB (!!) and it&#39;s being injected into the controller so we can now mock and test and actually use MongoDb in our project. Now we need to go and modify the AdminControllerBehaviors test which checks the result of calling the Categories action so that we check to see if there is a view model returned which contains a list of categories. That test now looks like :&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/3982696.js?file=gistfile1.cs&quot;&gt;&lt;/script&gt;

&lt;p&gt;Now if we run the specflow file now we&#39;re moving ahead again except we fail at:&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/3982770.js?file=gistfile1.txt&quot;&gt;&lt;/script&gt;

&lt;p&gt;This is because even though I know we are grabbing the categories from the DB we never actually saved them because we never had a test that demanded it. Now we do so lets extend the repository to add some persistence:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/3982962.js?file=gistfile1.cs&quot;&gt;&lt;/script&gt;

&lt;p&gt;Now we just need to go back and change the CategoriesAdd POST test so that we make sure we save the category into the DB:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/3983076.js?file=gistfile1.cs&quot;&gt;&lt;/script&gt;

&lt;p&gt;I&#39;ve also whizzed through and updated the test for the loading of the categories action so that we are asserting that we are returning some categories:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/3983099.js?file=gistfile1.cs&quot;&gt;&lt;/script&gt;

&lt;p&gt;And the updated CategoriesController now looks like (I&#39;m not suggesting this code is production ready but it passes this test!):

&lt;script src=&quot;https://gist.github.com/3983108.js?file=gistfile1.cs&quot;&gt;&lt;/script&gt;

&lt;p&gt;I also added the following to the Categories view:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/3983112.js?file=gistfile1.html&quot;&gt;&lt;/script&gt;

&lt;p&gt;Now that we have persistence using MongoDb (which I have never used before) and we have all of the IoC, MVC infrastructure stuff all set up and we can save categories I feel pretty good about the progress. Picking off the categories feature was going easy on myself because it was the easiest feature to drop into. But I also knew that this feature would also bare the brunt of the infrastructure bootstrapping side of stuff. Although this feature isn&#39;t 100% complete and the code that is there isn&#39;t exactly tight I am happy that it&#39;s a good grounding to build on. I&#39;m going to leave it there tonight. Next session I need to complete the feature and make the specflow test pass. I&#39;ll also set up a public deployment for the staging code - at some point I should hook this into TeamCity / Octopus deploy but that&#39;s further down the line.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/1045578399050849462/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2012/10/building-wwwbrightonsausagecocom_30.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/1045578399050849462'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/1045578399050849462'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2012/10/building-wwwbrightonsausagecocom_30.html' title='Building the www.brightonsausageco.com website - 30/10/2012'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-2206507595710351278</id><published>2012-10-30T12:44:00.001-07:00</published><updated>2012-10-30T14:33:13.075-07:00</updated><title type='text'>Building the www.brightonsausageco.com website - 29/10/2012</title><content type='html'>&lt;p&gt;This is a follow up to the 1st in this series: &lt;a href=&quot;http://iwayneo.blogspot.co.uk/2012/10/building-www.html&quot;&gt;http://iwayneo.blogspot.co.uk/2012/10/building-www.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the second &lt;a href=&quot;http://iwayneo.blogspot.co.uk/2012/10/building-www_16.html&quot;&gt;http://iwayneo.blogspot.co.uk/2012/10/building-www_16.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the third: &lt;a href=&quot;http://iwayneo.blogspot.co.uk/2012/10/building-www_26.html&quot;&gt;http://iwayneo.blogspot.co.uk/2012/10/building-www_26.html&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;Monday morning, 6:00am!! I left the site on Friday with a passing step which basically meant that the add category form wouldn&#39;t b0rk on submission. I know there&#39;s some blank spots to fill here (persistence) but I don&#39;t have a failing step yet so I&#39;m being ignorant to all these things - I&#39;ll tackle them when the tests require me to.&lt;/p&gt;

&lt;P&gt;From the feature file I can see that we are failing on the following step: &quot;Then I should see &quot;category1&quot; show up in the list of existing categories&quot; because when we click the Save button we get a 404 POSTing to /admin/categories/add. This was a simple coding error with inconsistent action names: the GET action was called CategoriesAdd and the POST was called Categories - so the routing wouldn&#39;t work POSTing to that URL - I need to get the routing tests to be more detailed.&lt;/p&gt;


&lt;p&gt;The good thing about allowing my development to be guided by small steps in a feature file is that I can easilly gain continuity and remain focused during disparate, micro sprints: TBD (Train Based Development!). To get this step passing I just need to add a redirect in the POST action so that we&#39;re headed back to the main category page where we can add the list of existing categories. Before I can add the redirect to the action I need to make sure I have a failing test:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/3982517.js?file=gistfile1.cs&quot;&gt;&lt;/script&gt;

&lt;p&gt;Here is what the action now looks like in order to make that test pass:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/3982521.js?file=gistfile1.cs&quot;&gt;&lt;/script&gt;

&lt;p&gt;We&#39;re still not out of the woods however - we still have a failing step and in fact - I think the woods just got darker! The reason we&#39;re still failing is because we aren&#39;t showing a list of existing categories. Existing categories is the first mention of real persistence - we hinted at the fact that we were storing the categories somewhere when we submitted the form but there was not concrete spec suggesting that was the case. Now we have the requirement to pluck categories out of somewhere. This means a decision on what storage mechanism must be made. Because I don&#39;t want the unecessary complication of RDBMS plus ORM I have already opted out of SQL. This leaves me with the 3 good doc based storage engines that I would choose from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RavenDb&lt;/li&gt;
&lt;li&gt;Mongo&lt;/li&gt;
&lt;li&gt;Riak&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And without much adoooo I can tell you that I have opted for MongoDb on the bases that it is free and has a large community. [installs mongo + NuGet for driver in both MVC and Behaviors projects]. So I&#39;m happy with the idea that I&#39;m going to be fetching a set of Categories from MongoDb. To do this I need to pass in a repository to the Admin controller which I can use to talk to the DB. This means going back to the first test and making the AdminController instantiation work with an IRepository&lt;Category&gt;:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/3982524.js?file=gistfile1.cs&quot;&gt;&lt;/script&gt;

&lt;p&gt;And the code for the AdminController now looks like:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/3982531.js?file=gistfile1.cs&quot;&gt;&lt;/script&gt;

&lt;p&gt;That gives us the ability but we&#39;re not doing so let&#39;s sort that. To display the categories we need to be able to get all of them from the repository. Let&#39;s go and add the IRepository&lt;T&gt;.GetAll() method then.&lt;/p&gt;

</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/2206507595710351278/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2012/10/building-wwwbrightonsausagecocom.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/2206507595710351278'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/2206507595710351278'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2012/10/building-wwwbrightonsausagecocom.html' title='Building the www.brightonsausageco.com website - 29/10/2012'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2889634717122112415.post-3836538111091626348</id><published>2012-10-26T09:58:00.001-07:00</published><updated>2012-10-30T14:33:22.050-07:00</updated><title type='text'>Building the www.brightonsausageco.com website - 26/10/2012</title><content type='html'>&lt;p&gt;This is a follow up to the 1st in this series: &lt;a href=&quot;http://iwayneo.blogspot.co.uk/2012/10/building-www.html&quot;&gt;http://iwayneo.blogspot.co.uk/2012/10/building-www.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the second &lt;a href=&quot;http://iwayneo.blogspot.co.uk/2012/10/building-www_16.html&quot;&gt;http://iwayneo.blogspot.co.uk/2012/10/building-www_16.html&lt;/a&gt;&lt;/p&gt;

Third day; Third blast at the site and it&#39;s been 11 days since I last looked at the code. I&#39;ve done a lot of client work and a lot of work on www.sonatribe.com in between then and now so i don&#39;t really have a clue where I left it! Looking at the selenium tests I can see that the site spins up and it tries to reach the admin/categories/add URL - there&#39;s no link so I guess that&#39;s as good a place as any to start.&lt;br /&gt;
Without getting anal about testing the UI by precompiling it and blah I&#39;m going to dive right in and add the link to the /admin page and get that step passing. It&#39;s as simple as:&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/3959877.js?file=gistfile1.html&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
One step further! Now the selenium test moves the browser to the admin/categories/add url but again there&#39;s no code to handle this. Again - all URLs start with a route so I look at the routing test and find there&#39;s no route test defined for this URL so I go ahead and add that:
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/3959884.js?file=gistfile1.cs&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
This test obviously fails as there&#39;s no route actually defined - tests don&#39;t make code durrrrrr... I&#39;ll add that to the BrightonSausageCoEcomms.RouteConfig class:&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/3959888.js?file=gistfile1.cs&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
Chuck in a cheeky little view in /Views/Admin/CategoriesAdd.cshtml with it&#39;s corresponding empty view method returning View() in the controler (test first obviously) and we&#39;re done! Spinning up the selenium test again lets us go another step further. Next line in the feature is: &quot;And I enter &quot;category1&quot; into the &quot;CategoryName&quot; textbox&quot;. This is again going to fail because we have no CategoryName text box so I&#39;ll add that&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/3959892.js?file=gistfile1.html&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
Spinning up the selenium tests again I can see the browser is taken to the add category page and the CategoryName is populated! It&#39;s almost too much to handle... There&#39;s also a checkbox to add so I&#39;ll do the same rinse and repeat until I get past that line. Once that&#39;s all good I fumble on the next big line: &quot;When I click the &quot;Save&quot; button&quot;. I&#39;ll add the save button then shall I?! Now the problem isn&#39;t really here - now that I added it - that line goes through ok... The problem is the next line: &quot;Then I should see &quot;category1&quot; show up in the list of existing categories&quot;&lt;br /&gt;
That&#39;s a pretty big statement! First up I added the submit button but since there&#39;s no form nothing happens. Even if there were it&#39;s not going anywhere as the controller has no clue what to do with such a request. Even if it did - what the hell does it do to make that sort of magic actually happen? Curiouser and curiouser... I&#39;ll add the form tag and see wha gwarn... The code now looks like:&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/3959896.js?file=gistfile1.html&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
Perfecto.. except an HTTP POST to that url goes nowhere - although we have the routing set up we only know how to accept the HTTP GET verb - lets make the controller able to deal with the POST verb by adding another action.&lt;br /&gt;
This action is going to be slightly different because we&#39;re going to be recieving data from the forms post. The name of the category and the enabled value will be sent to the action in the form of a view model. A view model is just that - it&#39;s the model specific to the view. A model is just a data structure. So our test will need to pass this viewmodel into the action. And the action will need to be decorated with the HttpPost attribute and it will return a redirect to the admin/categories view but that&#39;s pretty much all we know for now. Here&#39;s the test (I&#39;m leaving out the attribute part for now as I&#39;m on the train and the reception is awful!):&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/3959874.js?file=gistfile1.cs&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
Notice we now have a CategoriesAddViewModel - I used resharper to drive the definition of that so by the time I had the test written I had the overload for Categories(CategoriesAddViewModel) created and the CategoriesAddViewModel inside the mvc project - all be it a blank class - it&#39;s enough to get this test passing for now.
&lt;br /&gt;
That&#39;s it for now - I&#39;m done for today. Not a lot achieved but I&#39;m fried!!</content><link rel='replies' type='application/atom+xml' href='http://iwayneo.blogspot.com/feeds/3836538111091626348/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://iwayneo.blogspot.com/2012/10/building-www_26.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/3836538111091626348'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2889634717122112415/posts/default/3836538111091626348'/><link rel='alternate' type='text/html' href='http://iwayneo.blogspot.com/2012/10/building-www_26.html' title='Building the www.brightonsausageco.com website - 26/10/2012'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>