Meet the new love of my life, her name is Aurelia.io

YEZZZZ! I can finally start spreading the love of the goodness that is named Aurelia, with which I’ve secretly been in love with for a while now. I meant to write about her last week, but an NDA contract prevented me from bragging about her beauty before Rob did.

Wait, who’s rob? Who’s Aurelia? What does this have to do with anything? How does it affect me? And you?

Rob…

Rob Eisenberg’s one of the most genius architects of our era. I’ve been a fan of his work since the Caliburn days, a framework he wrote which made MVVM for WPF/SL/WP/… as easy as possible by reducing boilerplate code and using conventions instead.  Over the last couple of years, Rob created an HTML/JS framework named DurandalJS, a masterpiece which never got the credit it deserved.  Well, except from the fine folks at Google, who hired Rob to help build out the next generation of their popular AngularJS framework.

Late 2014 however, Rob left Google because his colleagues were not as visionary as he was (my words, he was more diplomatic in his goodbye message) and started assembling a team of world-renowned experts (and me) to focus on the first of the next-generation javascript frameworks…

Aurelia…

So ok, enough about my man-crush on Rob and his hyperthreaded brain, let’s focus on this beauty of this open-source framework that’s called aurelia. Why would this framework be any better than it’s “competitors”, like EmberJS or the next AngularJS?

I could copy its major strengths from the aurelia.io homepage,  but I’d rather highlight three really important ones (which is not to say you shouldn’t head over to aurelia.io right now to look at her in her full glory):

  1. ES6. If clarification is needed: ES6 is short for EcmaScript 6, the next generation of javascript. It’s just a (finished) specification at this point, since none of the major browsers actually fully support ES6 yet, so aurelia is written in ES6 but then ‘compiled’ into current javascript (ES5).  In the gitter channel, an open chat channel where you are all welcome to discuss anything aurelia, I’ve seen Rob look at the next-next HTML (thus: HTML7) specs, to analyse how we should build the best (most future-compatible) API’s. Long story short: Aurelia is ready for the future, today.
  2. MV* architecture with conventions. Lots and lots of conventions. Aurelia assumes a ton of things out-of-the box, and you can add your own conventions. Got a VM named customer.js and a V called customer.html? Aurelia saw what you were trying to do there. It wasn’t that hard to get either, so why should developers have to write code/configuration to bind those two together? Write code only once and especially: avoid writing unnecessary code (and yes if you don’t like a convention you can always override it). Which seamlessly brings us to the next highlight:
  3. Extensible HTML… As a technology enthusiast, I get a nerdgasm just looking at aurelia. However if one must build large LOB applications, writing HTML all day is too granular to make any kind of progress. However, thanks to aurelia’s extensible HTML compiler, things like <barChart xAxis.bind=”months” yAxis.bind=”sales” /> all the sudden, become valid HTML.

Pause.

Think about that for a moment…

This means that me and you and anyone can write reusable building blocks which are an abstraction level higher than HTML, without actually hiding/losing control over the underlying technology stack!

Me? You?

You and me shared a love for RAD,that’s for sure. And Aurelia is not RAD at all and certainly not a better version of anything like LightSwitch.

Yet.

Aurelia is only the tip of the arrow currently loaded on my bow.

Late last year, three of my LightSwitch customers (and two more are in discussing phase) and I actively started looking at a newer tech stack to phase out LightSwitch if the day would ever come LS is announced ‘dead’.

We looked at so many RAD frameworks and were impressed by a quite a few, but convinced by none. Most RAD frameworks work great for small and simple CRUD apps, but suffer from either poor testability, modularity, scalability, extensibility, most likely all of the above.

Hence, we’re currently building out a super-framework ourselves based on Entity Framework, Web API (CQRS architecture) and aurelia. I use the term super, not only because it is super and kicks ass, but mostly because the intent of our venture is to take all boilerplate and repetitiveness out, and build out applications using a ‘higher abstraction of code’. A super-set of traditional code. Much like LightSwitch used graphical designers as a higher abstraction of all the code goodness it generated “under the hood”, however this time we’ll still have full control of what’s on top of the hood, “under the hood”, and the hood itself.

This’ll keep me very busy during the first part of 2015 (during which I still have LightSwitch projects and will share whenever I can share any LS goodies), then afterwards will undoubtedly be a large part of my future for quite some years to come. Perhaps your future too, as it’ll all be open for your LOB app development pleasures. If you’re in a position where you need a modern, open and future-proof stack for business app development now, please do get in contact so we can work together asap, or at least let me know about your special requirements so I can make sure we can develop this thing as smart as possible.

The future starts today. I haven’t been so excited about tech since a few years ago and am really happy to be a part of, from the start of, this beauty.  Aurelia really is the first of a next generation of frameworks, you owe it to your future self to take her out for a spin. If not today, then tomorrow.

If she looks too high-tech or too granular for you or to make LOB apps today, give it a little bit of time too. We’re at the start of this journey and I’ll put in the effort to make sure she’s worth the venture!

 

So what’s the deal with that announcement?!?

I wish I could tell you. No seriously, I deeply apologize for the extreme hopes my last post left, but I’d be breaking my NDA if I’d talk of this before the official announcement, which was planned for today but apparently needs a little more preparation before it’ll take public form.

Perhaps, no surely, I shouldn’t have put a specific time-frame in my last post.

I hope tomorrow, or later this week, I can undo this disappointment of this post and wash off the smell of rotten tomatoes, that you may now rightfully throw at me, by sharing the goodness that is this novelty with you!!

It’s 2015, and LightSwitch is dead… Now what?

In case I haven’t seen you yet this year (and chances of that are high since I spent almost all of my time in my hammock so far), happy new year to you!

Just two weeks ago, the year turned 2015, always a great opportunity to reflect upon the past, and to learn lessons for the future.

And what a harsh lesson 2014 has taught us: LightSwitch is dead.

There, it’s out, and apparently I’m the first one to publicly carve these words in cyberspace. But unfortunately, a rose by any other name is still a rose, so let’s just call it what it is: dead.

Stage 1: denial

Let me first shed some light on why I consider the lightswitch to be in eternal off-state: there have been no new substantial developments since the HTML client (you weren’t really sitting around waiting for the O365 stuff now, were you), the ‘team blog‘ hasn’t had any meaningful posts in over half a year and many of the active community bloggers have either changed blogs, or have only been writing about using LightSwitch in combination with other technologies (with most of the focus on the other technologies).

If you want to try denying a technology being dead simply due to lack of evolution, then here’s a quote from the team latest post:

we’re currently planning to actively engage with the community and start our discussions on the roadmap in the middle of next year

Our love is dead, and you have been feeling it for a while now.

Perhaps you haven’t admitted it to yourself. Perhaps you are still in denial, but in this fast-paced world of ever-evolving technologies and agile methodologies, I cannot consider the product of a team that shifts priorities for over a year as the solid foundation for my own work.

Stage 2: anger

If you want to, please do take a quick break from reading now to go to the gym, box club, or whatever else you usually do to vent your anger. If you happen to have any pitchforks and/or torches, you could even join the rest of us in our march in the MSDN forums too, there’s a lot of anger already shared over there.

Stage 3: bargaining

We’re still reflecting the pain of 2014. Those of us who were able to overcome denial and anger stages, started bargaining. End of September, I’ve personally sat down with Microsoft folks to talk about the idea of how providing limited extension points so the community would allow us to create a toolkit to address a lot of the missing features. Others have publicly pled to open-source the entire stack, which would fit in great with MSFT’s latest vision and announcements about open-sourcing parts of their stack.  Some have threatened to mail Mary Jo Foley, others to mail Somasegar, in a passive-aggressive attempt to bargain or even blackmail the team into action.

Unfortunately, none of that led to change.

Stage 5: Acceptance

You skipped stage 4: depression.

You just scrolled up and noticed I did not write about stage 4: depression.

Stage 4 in the Kubler-Ross model (‘the five stages of grief’) describes the emotions of sadness, fear and uncertainty that start to develop during the earlier stages.  Feeling these emotions is natural, personal, and shows that we are slowly entering the final stage: acceptance.   So let’s cut to the point and discuss what acceptance could bring, for me and my relationship with you, my highly valued readers, and you’ll see there is very little sadness to be found.

  • LightSwitch being dead does not mean it does not have a future. At some point, probably end 2015 or early 2016, we’ll see a next version from the same MSFT team. Personally I don’t think they’ll actually revive LightSwitch as some kind of frankenstein monster with new body parts, but instead reuse only their experience can into a new product with a new name and marketing strategy.
  • LightSwitch being dead does not mean it ceases to be important in 2015. Personally I have a ton of ongoing projects and consulting opportunities, and they all still stand valid. LightSwitch has always been a tool to create CRUD-oriented, small to medium sized applications, and that is still true regardless of the evolution of the platform itself.  If LightSwitch is the right answer to a particular problem, then File>New>LightSwitch Application here I come! Or as the team puts it:

If the support we have in Visual Studio 2015 meets the needs of your application, then you should feel confident in developing with LightSwitch or Cloud Business Apps as we build out the roadmap

  • This implies an ongoing commitment to my own blog as well. I’ve always blogged strictly about my own professional experiences, and if I encounter anything worth sharing and I find the time to do so, I will gladly blog it! Being it about LightSwitch, or something else…

So there’s the big cliffhanger… What something else?

What now?

There’s not much presently existing that can match the development speed of LightSwitch. However, because LightSwitch does not offer modularity, has bad testability and devastating performance for medium to large applications, I’ve been looking for alternatives to answer challenges that LightSwitch can not…

And I have found it too.

I have found my sweetspot combination of a technology stack that is completely open source (no longer dependent on MSFT management decisions), highly scalable, highly modular, highly performant, fully customizable but yet offers incredible speed of development.

“No way”, I hear you say, “Such a thing does not exist! If it existed, I would have surely heard about it”.

And you are right in saying so. Such a thing does not publicly exists… Yet. But so far, I’ve been mainly writing about 2014, and 2015 is still so very young. This coming Monday, in an announcement that is not my own, the first tip of this veil of secrecy that holds my future, and perhaps yours, will be lifted…

Leave my endpoints alone!

By default, LightSwitch apps expose the server tier by one public OData endpoint per data-source. Both the desktop and the HTML client make direct calls to these .svc urls to retrieve the data they need.

So here’s an interesting question I got this afternoon: this means that all data is publicly visible?

Well, yes and no. The endpoints are publicly visible, however when you access them, the LightSwitch (actually asp.Net) framework’s authentication and authorization will kick in to protect the data (authentication determines if you can access the endpoint, authorization determines if you can access that particular entity set and what records you can access).

However, there’s some cases where you want to protect your endpoints even more, for example when you’re forced to build an application without authentication, or if you want to prevent a user with a valid username&pw (or windows credentials) to access the data from outside your own apps.

In that case, you can add a custom IHttpModule. This would include registering the module in your web.config…

  <system.webServer>  
    <modules>
      <add name="CustomHeaderModule" type="StrongNamespace.HttpModules.CustomHeaderModule"/>
    </modules>

and adding whatever code you want to your server project…

using System;
using System.Web;

namespace StrongNamespace.HttpModules
{
    public class CustomHeaderModule : IHttpModule
    {
        public void Init(HttpApplication application)
        {
            application.PostReleaseRequestState += new EventHandler(application_PostReleaseRequestState);
        }

        public void Dispose()
        {
        }

        void application_PostReleaseRequestState(object sender, EventArgs e)
        {
            if (HttpContext.Current.Request.Url.ToString().ToLower().Contains(".svc") 
                && !HttpContext.Current.Request.Url.ToString().ToLower().Contains("authenticationservice.svc")))
            {
                var referrer = HttpContext.Current.Request.UrlReferrer;
                if (referrer == null)
                    throw new Exception("Not allowed to access this data"); 
                else if (referrer.Host != HttpContext.Current.Request.Url.Host)
                    throw new Exception("Not allowed to access this data"); 
            } 
        }
         
    }
}

The above sample will block any calls to the .svc endpoints (your OData endpoints that expose your data) if the call is not made from a site within the same domain.

This hasn’t been production tested yet (and honestly you’ll probably need to customize the business rules a bit) but at first sight seems not to interfere with the LS desktop (OOB or inB) or HTML client, but do blocking any other calls.

It’s not really ‘web-friendly’ either, in production code I’d suggest rewriting the response to a 403-forbidden instead. But then again who gives a fuck about http status codes… (*grabs popcorn*)

 

Keep rocking LS!
Jan

Preventing a screen from closing in the LightSwitch desktop client

Every day, one of our end-users opens a screen and prints out all open support tickets for that day. When the tickets are printed, we want to make sure those tickets are marked as ‘printed’ on the server, so we have a button on the screen that lets her do just that:

        //Screen has one 'state' boolean
        private bool isMarkedAsPrinted = false; 

        //Normal code behind a button to mark tickets as printed
        partial void MarkAsPrinted_Execute()
        {
            if (!isMarkedAsPrinted)
            {
                foreach (var ticket in this.Tickets)
                    ticket.IsPrinted = true;
                this.Save();
                this.isMarkedAsPrinted = true; //Don't forget to change the screen's state
            }
            else
                this.ShowMessageBox("The tickets are already marked as printed.");
        } 

Sometimes, she’ll forget to press that button, so when the screen closes we want to check if the tickets are printed.  The screen has a ‘Write Code’ button for the _Closing event, and it is passed a bool called ‘cancel’. If you set this bool to ‘true’, the screen closing event will be cancelled.

        //Excecuted when the screen is closed
        partial void PrintTicketScreen_Closing(ref bool cancel)
        {
            //Cancel all closing events unless we set the 'reallyClose' flag 
            cancel = !(isMarkedAsPrinted); 
        }

That works like a charm. Once she prints, the isMarkedAsPrinted boolean is set to true, and the screen can close. When she forgets to press the button, the screen will not close no matter how many times the ‘x’ is clicked.

From a UX perspective though, this is rather weird. The application will just feel like it doesn’t respond to her trying to close the screen. We could have the screen show a message box saying she needs to print first, or better yet: asking her if she wants to print:

        //Screen has one 'state' boolean
        private bool isMarkedAsPrinted = false; 

        //Normal code behind a button to mark tickets as printed
        partial void MarkAsPrinted_Execute()
        {
            if (!isMarkedAsPrinted)
            {
                foreach (var ticket in this.Tickets)
                    ticket.IsPrinted = true;
                this.Save();
                this.isMarkedAsPrinted = true; //Don't forget to change the screen's state
            }
            else
                this.ShowMessageBox("The tickets are already marked as printed.");
        } 
         
        //Excecuted when the screen is closed
        partial void PrintTicketScreen_Closing(ref bool cancel)
        {
            //Cancel all closing events unless we set the 'isMarkedAsPrinted' flag 
            cancel = !(isMarkedAsPrinted);

            if (cancel)
            {
                var answer = this.ShowMessageBox("Would you like to mark these tickets as printed before closing the screen?", "Mark tickets as printed?", MessageBoxOption.YesNoCancel);
            switch (answer)
            {
                case MessageBoxResult.Cancel:
                    //Simply let the screen stay open in current state
                    break;
                case MessageBoxResult.Yes:
                    //Execute mark as printed, then close screen automatically
                    this.MarkAsPrinted_Execute();
                    cancel = false;
                    break;
                case MessageBoxResult.No:
                    //User doesn't want to mark as printed, close screen
                    cancel = false;
                    break;
                default:
                    throw new Exception("Unexpected result.");
            }
            }
        }

Something weird will happen when you run through the code though… When closing the screen, if the tickets haven’t been marked as printed, the dialog will be displayed to remind the user to save. However, half a second later, the screen will close anyways.

Here’s the rub: your screen’s _Closing event is given about one or two seconds to finish executing. When it does not finish executing within that timeframe, the LightSwitch framework will close every open dialog on the screen (including your ‘would you like to mark the tickets as printed’ one), and close the screen anyway.

The workaround is to make sure the _Closing event code returns immediately, but cancels the closing, on first run. Before we return from that method though, we’ll kick off a background worker. This background worker will ask the screen’s logical dispatcher to show the ‘would you like to mark the tickets as printed’ dialog. This request will be queued by the screen’s logical dispatcher and executed whenever it has time (read: whenever it is done not closing your screen).

Once the tickets are marked as printed, or the user refuses to mark the tickets as printed (perhaps the screen was opened by accident in the first place, or the printer ran out of paper), we’ll flip a boolean and close the screen programmatically. The second time the screen’s _Closing event code is executed, it’ll pick up this boolean (or notice the tickets have been marked as paid) and let the screen close this time:

        //Screen has two 'state' booleans
        private bool isMarkedAsPrinted = false;
        private bool userDoesNotWantToMarkAsPrinted = false;

        //Normal code behind a button to mark tickets as printed
        partial void MarkAsPrinted_Execute()
        {
            if (!isMarkedAsPrinted)
            {
                foreach (var ticket in this.Tickets)
                    ticket.IsPrinted = true;
                this.Save();
                this.isMarkedAsPrinted = true; //Don't forget to change the screen's state
            }
            else
                this.ShowMessageBox("The tickets are already marked as printed.");
        } 
         
        //Excecuted when the screen is closed
        partial void PrintTicketsScreen_Closing(ref bool cancel)
        {
            //Cancel all closing events unless we set the 'isMarkedAsPrinted' flag OR 'userDoesNotWantToMarkAsPrinted'
            cancel = !(isMarkedAsPrinted || userDoesNotWantToMarkAsPrinted);

            if (cancel)
            {
                //If this method takes longer than a second, the LS framework will close all dialogs as 'cancelled'
                //Hence, we start a backgroundWorker so that we can return from this method straight away
                var sleepy = new BackgroundWorker();
                sleepy.DoWork += (s, e) =>
                {
                    //This code will execute on the thread of the background worker, so
                    //we must ask the screen's dispatcher to invoke the 'askPrintBeforeClosing' 
                    //instead of asking this on the background worker's thread
                    this.Details.Dispatcher.BeginInvoke(() => { askPrintBeforeClosing(); });
                };
                sleepy.RunWorkerAsync();
            }
        }

        //Helper method
        void askPrintBeforeClosing()
        {
            var answer = this.ShowMessageBox("Would you like to mark these tickets as printed before closing the screen?", "Mark tickets as printed?", MessageBoxOption.YesNoCancel);
            switch (answer)
            {
                case MessageBoxResult.Cancel:
                    //Simply let the screen stay open in current state
                    break;
                case MessageBoxResult.Yes:
                    //Execute mark as printed, then close screen automatically
                    this.MarkAsPrinted_Execute();
                    this.Close(false);
                    break;
                case MessageBoxResult.No:
                    //User doesn't want to mark as printed, close screen automatically
                    userDoesNotWantToMarkAsPrinted = true;
                    this.Close(false);
                    break;
                default:
                    throw new Exception("Unexpected result.");
            }
        }

Keep rocking LS!
Jan

 

 

 

 

 

Loading of collections in the desktop client on a ‘need to see’ basis.

I just spent my Saturday afternoon on a really fun puzzle: loading data on a ‘need to see’ basis. This code hasn’t been production tested at all (use at own risk), but is way too fun not to share.

Challenge

The LightSwitch desktop application can sometimes feel really sluggish, this is partly because collections on a screen ‘execute automatically by default’, this means that they are loaded automatically if they are used in the UI, even if that part of the UI is not visible (for example if the grid is on a tab that is not active at the moment). So here’s the challenge: can we speed up the application by loading data on a ‘need to see’ basis? Can we make our application intelligent enough so that data is loaded if and only if it needs to be visualized?

I have a screen that contains a couple of collections, let’s say that Foo has a one-to-many relationship with Bar and I made a list-detail screen with three tabs, one containing the Foo’s details, one containing the Foo.Bars as a Data Grid, and one containing the Foo.Bars as a List.Image 247Or at runtime:Image 248

When I go to the Foos List Detail, using Fiddler or another network sniffing tool, I can see that both the Foos, and the first Foo’s.Bars are automatically loaded.Image 251

(Well: obviously, because in the screen designer both the Foo and Bars collection have their ‘Auto Execute Query’ selected…)Image 243

Making your application smarter

Add a new class to your client project, named ‘LoadOnDemandExtensions’. Copy the source for that class from this gist.  It’s in C#, but here’s a good link for our VB friends.

(For those of you that actually clicked the VB link, I’m sorry. That was mean. To make it up, I posted the VB code at the bottom of that same gist ;-) )

Step 2: right-click on the client code in Solution Explorer, hit ‘View Application Code’ to open your client application code. In the Application_Initialize method, write:

C#
    public partial class Application
    {
        partial void Application_Initialize()
        {
            this.LoadVisualCollectionsOnDemand(); 
        } 
    }
VB
    Public Class Application

        Private Sub Application_Initialize() 
            Me.LoadVisualCollectionsOnDemand() 
        End Sub
    End Class

End of blog post.

No, really!  Press F5 and see how your application is behaving when it starts up now:Image 249

Even though the ‘Auto Execute Query’ is still on for Bars, the Lightswitch application didn’t load them because they aren’t visible at the moment. As soon as you hit the ‘Bars’ tab:

Image 250

 What is this sorcery?

The LoadVisualsOnDemand method will dig in the LightSwitch extensibility model to grab a hold of the NotificationService. It’ll ask that NotificationService to be notified whenever a screen is opened.

When a screen is opened it’ll loop over every collection on that screen and use some nifty hacking techniques (reflection is forbidden in SL in-browser sandboxed applications, but there’s a little loophole) to turn off ‘Auto Execute Query’ on the loader of each collection.

Also, it will search the screen for any ‘ContentItem’ (that is the right-hand side of your screen designer, which looks like a ‘visual tree’) that is displaying a collection (in reality this’ll be your List, Data Grid, or SuperDynamicGrid really). It will ask to be notified whenever such a control becomes visible/hidden. As soon as this happens, it’ll re-hack the VisualCollection to turn the ‘Auto Execute Query’ back on/off based on visibility.

This results in your collection becoming loaded, working (apparently paging, sorting and quicksearch do not work using other screen performance tips) and staying in sync (for example, you click on another Foo, this causes the Bars to be reloaded)

tl;dr; LightSwitch will now ignore your ‘Auto Execute Query’ attempts and turn that on/off depending on visibility.

Noteworthy caveat

Bugs incoming! This method will control the ‘Auto Execute Query’ property on all collections, every screen. However, some of your code might already depend on the implicit assumption that a screen collection is loaded. Screen your screen code files, and if you spot a spot where you access a collection in code, make sure it’s loaded using this other nifty extension method.

C#
        partial void Method_Execute()
        {
            this.Bars.LoadIfNeeded();
            var count = this.Bars.Count; //for example
        }
VB
        Private Sub Method_Execute()
            Me.Bars.LoadIfNeeded()
            Dim count = Me.Bars.Count 'for example
        End Sub

But what if…

What if I don’t want this behavior on every screen?

Instead of turning this on on an application level, you can activate it on a screen-by-screen basis.

On those screens where you need it, turn off the ‘Auto Execute Query’ manually on each collection. Then, write this in the screens _Activated code:

C#
        partial void FoosListDetail_Activated()
        { 
            this.LoadCollectionsOnDemand();
        }
VB
        Private Sub FoosListDetail_Activated()
            Me.LoadCollectionsOnDemand()
        End Sub

What if I don’t want this behavior on every collection?

Instead of turning this on on an application or on a screen level, you can activate it on a collection-by-collection basis.

On those screens where you need it, turn off the ‘Auto Execute Query’ manually on those collections. Then, write this in the screens _Activated code (repeat for every collection):

C#
        partial void FoosListDetail_Activated()
        {
            this.Bars.LoadOnDemand();
        }
VB
        Private Sub FoosListDetail_Activated()
            Me.Bars.LoadOnDemand()
        End Sub

 

Keep rocking LS!

(But only the visible parts)

Jan

 

 

 

Coding tips #1: tell, don’t ask

As part of a new assignment, I have the privilege of working with a new team. Hey teamies! One of the things the team and I really want, is to make sure our code is clean and manageable. Hence, I’ll be putting up a coding tip once in a while on how you can improve your code for readability and maintainability.

Tell, don’t ask, is a reminder that you should endeavor to tell objects what you want them to do; do not ask them questions about their state, make a decision, and then tell them what to do (source).

In the LightSwitch world, entities are our models. All too often we are tempted to write code that asks the models about their state, then we take decisions about that state. For example:

if(currentCustomer.Orders.Any(c => c.ClosingCode.Code == "1209" || c.ClosingCode == "1304")
{
  //Preferred customer, add discount
  //More code here...
}

This works beautifully. It’s fast to write, any other developer can clearly read what’s going on, and it executes in 0ms flat (give or take a few ticks).

There’s a couple of issues with this type of coding though… First of all, how about readability? Can any developer clearly read what’s going on? Well, read: yes. Understand: no. If he/she does not know what these closing codes mean, he/she’s shit out of luck.  Secondly, there’s a maintenance issue. What if a closing code changes? What if a third closing code is added that makes a customer preferred, or one is removed? What if we want to support multiple tenants, and for one tenant closing code “1209” makes the customer preferred, but for another one it doesn’t?

In all of these likely cases, we’d have to loop over our entire code base and find all the spots where we ‘ask’ the object about its state, and then take actions accordingly, and update that code…

We can get a few quick wins in maintainability and readability by adding a new field to the ‘ClosingCode’ entity:

if(currentCustomer.Orders.Any(c => c.MakesCustomerPreferred)
{
  //Preferred customer, add discount
  //More code here...
}

It seems like just a small change, but adding this field to the ClosingCodes really improves readability (as a new developer, I don’t have to know about the actual codes), we can add/edit/delete closing codes that make a customer preferred, and we can support different closing codes for multiple tenants.

Almost there though: our code still has the hard-coded idea that a customer can only be preferred based on closing codes. What if one day we also want to make a customer preferred based on other parameters, like ‘simply being a nice person’?  This additional refactoring requires you to take a decision though: refactoring takes time, and time costs money. Are these scenarios likely to happen, or are they more of a ‘one day it’d be nice to have this’ (YAGNI – you ain’t gonna need it).

If there is a very real chance that these scenarios might happen, you can further reduce the complexity of the code by moving the ‘IsCustomerPreferred’ state into the Customer object, instead of keeping it hidden inside the OrderClosingCode table:

if(currentCustomer.IsPreferred)
{
  //Preferred customer, add discount
  //More code here...
}

Obviously, you’ll need additional code, for example when an order is closed, to tell the customer it is preferred or take that status away.

 

Keep rocking LS!

 

Jan