Wednesday, 7 December 2016

Fat vs thin events

StackOverflow has a good article discussing the pros and cons of fat vs thin events. I thought I'd repeat it here.

The question raised was:

"When raising an event, which pattern is the most suited:
  1. Name the event "CustomerUpdate" and include all information (updated or not) about the customer
  2. Name the event "CustomerUpdate" and include only information that have really been updated
  3. Name the event "CustomerUpdate" and include minimum information (Identifier) and/or a URI to let the consumer retrieves information about this Customer.
I ask the question because some of our events could be heavy and frequent."
And the response was:
Name the event "CustomerUpdate"
First let's start with your event name. The purpose of an event is to describe something which has already happened. This is different from a command, which is to issue an instruction for something yet to happen.
Your event name "CustomerUpdate" sounds ambiguous in this respect, as it could be describing something in the past or something in the future.
CustomerUpdated would be better, but even then, Updated is another ambiguous term and is nonspecific in a business context. Why was the customer updated in this instance? Was it because they changed their payment details? Moved home? Were they upgraded from silver to gold status? Events can be made as specific as needed.
This may seem at first to be overthinking, but event naming becomes especially relevant as you remove data and context from the event payload, moving more toward skinny events (the "option 3" from your question, which I discuss below).
That is not to suggest that it is always appropriate to define events at this level of granularity, only that it is an avenue which is open to you early on in the project which may pay dividends later on (or may swamp you with thousands of event types).
Going back to your actual question, let's take each of your options in turn:
Name the event "CustomerUpdate" and include all information (updated or not) about the customer
Let's call this "pattern" the Fat message.
Fat messages represent the state of the described entity at a given point in time with all the event context present in the payload. They are interesting because the message itself represents the contract between service and consumer. They can be used for communicating changes of state between business domains, where it may be preferred that all event context be present during message processing by the consumer.
Advantages:
  • Self-consistent - can be consumed entirely without knowledge of other systems.
  • Simple to consume (upsert).
Disadvantages:
  • Brittle - the contract between service and consumer is coupled to the message itself.
  • Easy to overwrite current data with old data if messages arrive in the wrong order.
  • Large.
Name the event "CustomerUpdate" and include only information that have really been updated
Let's call this pattern the Delta message.
Deltas are similar to fat messages in many ways, though they are generally more complex to generate and consume. Because they are only a partial description of the event entity, deltas also come with a built-in "assumption" that the consumer knows something about the event being described. For this reason, they may be less suitable for sending outside a business domain, where the event entity may not be well known.
Advantages:
  • Smaller than Fat messages
Disadvantages:
  • Brittle - similar to the Fat message, the contract is embedded in the message.
  • Easy to overwrite current data with old data.
  • Complex to generate and consume
Name the event "CustomerUpdate" and include minimum information (Identifier) and/or a URI to let the consumer retrieves information about this Customer.
Let's call this the Skinny message.
Skinny messages are different from the other message patterns you have defined, in that the service/consumer contract is no longer explicit in the message, but implied in that at some later time the consumer will retrieve the event context. This decouples the contract and the message exchange, which is a good thing.
This may or may not lend itself well to cross-business domain communication of events, depending on how your enterprise is set up. Because the event payload is so small (usually just an ID), there is no context other than the name of the event on which the consumer can base processing decisions; therefore it becomes more important to make sure the event is named appropriately, especially if there are multiple ways a consumer could respond to a CustomerUpdated message.
Additionally it may not be good practice to include an actual resource address in the event data - because events are things which have already happened, event messages are generally immutable and therefore any information in the event should be true forever in case the events need to be replayed. In this instance a resource address could easily become obsolete and events would not be re-playable.
Advantages:
  • Decouples service contract from message.
  • Information about the event contained in the event name.
  • Naturally idempotent (with time-stamp).
  • Generally tiny.
  • Simple to generate and consume.
Disadvantages:
  • Consumer must make additional call to retrieve event context - requires explicit knowledge of other systems.
  • Event context may have become obsolete at the point where the consumer retrieves it, making this approach generally unsuitable for some real-time applications.
When raising an event, which pattern is the most suited?
I hope you have by now realised that the answer to this is it depends. I will stop here - yours is a great question because you could probably write a book while answering it, but I hope you found this answer helpful.

Saturday, 19 November 2016

NServiceBus Saga persisted with NHibernate errors with "The following types may not be used as proxies"

If you use an NServiceBus Saga persisted with NHibernate and you use the Saga Data as per the sample code it errors with "The following types may not be used as proxies"

The sample code is:

public class OrderSagaData :
    IContainSagaData
{
    public Guid Id { get; set; }
    public string Originator { get; set; }
    public string OriginalMessageId { get; set; }
    [Unique]
    public string OrderId { get; set; }
}

The solution is to mark all the properties as virtual.

Thursday, 17 November 2016

NServiceBus doesn't create TimeoutEntity or Subscription databases, even in Integration mode

Note: this refers to NServiceBus 4.6.5. Other versions may differ.

I had a problem whereby I was running an NServiceBus server on a new machine. It was using the
NHibernate ORM with SQL Server as the persistence store.

However, even with the endpoint running in NServiceBus.Integration mode, I found that the underlying databases were not being created. On starting the endpoint it would throw an ADOException with messages like

"could not execute query\r\n[ SELECT this_.Id as y0_, this_.Time as y1_ FROM TimeoutEntity this_ WHERE this_.Endpoint = @p0 and (this_.Time >= @p1 and this_.Time <= @p2) ORDER BY this_.Time asc ]\r\n  Name:cp0 - Value:SchemaCreator  Name:cp1 - Value:17/11/2006 15:10:37  Name:cp2 - Value:17/11/2016 15:10:37\r\n[SQL: SELECT this_.Id as y0_, this_.Time as y1_ FROM TimeoutEntity this_ WHERE this_.Endpoint = @p0 and (this_.Time >= @p1 and this_.Time <= @p2) ORDER BY this_.Time asc]"

as it was unable to read from the (not yet created) database.

The startup code was below:


        
        private static IBus SetupBus()
        {
            Configure.Serialization.Json();
            return Configure
                    .With(GetAssembliesToScan())
                    .DefaultBuilder()
                    .UseNHibernateSubscriptionPersister()
                    .UseNHibernateTimeoutPersister()
                    .UseNHibernateSagaPersister()
                    .UseNHibernateGatewayPersister()
                    .UseInMemoryGatewayDeduplication()
                    // Hack the unobtrusive conventions to force the console to work.
                    .DefiningEventsAs(t => t.Namespace != null && t.Namespace.StartsWith("Orders.Contracts"))
                    .UnicastBus()

                    // Load handlers to allow subscriptions to be set up.
                    .LoadMessageHandlers()
                    .CreateBus()
                    .Start(() => Configure.Instance.ForInstallationOn().Install());
        }

        private static Assembly[] GetAssembliesToScan()
        {
            return new[]
            {
                typeof(IOrder).Assembly,
                typeof(Program).Assembly
            };
        }

In the end after stepping through lots of NServiceBus and NServiceBus.NHibernate code I found the answer to the problem.
NServiceBus scans assemblies for code implementing the INeedToInstallSomething interface. The NServiceBus.Unicast.Subscriptions.NHibernate.Installer.Installer class implements this interface and it is this which calls the NHibernate SchemaUpdate.Execute() method.

However, despite the application's XML configuration referring to NHibernate and the code above explicitly defining the NHibernate persister, the installation code is not run because the implementing classes have not been scanned.

The solution is to modify the code GetAssembliesToScan method above to include the NServiceBus.NHibernate assembly.


        
        private static Assembly[] GetAssembliesToScan()
        {
            return new[]
            {
                typeof(IOrder).Assembly,
                typeof(Program).Assembly,
                typeof(NServiceBus.Unicast.Subscriptions.NHibernate.Installer.Installer).Assembly
            };
        }

Additionally, if your endpoint works with a Saga you need to scan the assembly that contains the SagaData.

public static IEnumerable AssembliesToScan
        {
            get
            {
                yield return typeof(IOrderCreated).Assembly;
                yield return typeof(OrderSagaData).Assembly;
                yield return typeof(NServiceBus.Unicast.Subscriptions.NHibernate.Installer.Installer).Assembly;
            }
        }

Wednesday, 16 November 2016

Samsung Galaxy 5 (SM-900F) doesn't support 4G in the USA

Having paid for a data upgrade for EE in the USA - I discovered the Samsung Galaxy 5 - SM900F, doesn't support the 4G frequencies in the USA.

Friday, 4 November 2016

Virgin Media CGNV4 - a security risk?

I've noticed that the Hitron CGNV4 router used by Virgin Media for its high-speed broadband has port 8080 open. So far I've been on the phone to them for 45 minutes and nobody knows what it's for!

Sunday, 30 October 2016

Updating the Galaxy Tab S to support adopted storage

Grrr. Samsung have purposfully disabled adopted storage for the Galaxy Tab S, meaning that all of the 16GB rapidly fills, especially when app developers lazily prevent the app to be moved to external storage.

I tried connecting the tablet via USB and running the
adb shell sm set-force-adoptable true
command, but this doesn't work.

There are steps to configure Adopted Storage which involves TWRP and the Storage Enabler. And it appeared to work in large - after following the steps I had the option to install a card as internal memory. But I had a random error on the Settings screen after formatting the SD card and also when copying files to the phone it still said "Out of disk space" error. At that point, after 3 hours of trying I gave up and got on with my life.

Lessons learned:
Samsung create great phones and tablets, but the firmware sucks. And there isn't a phone out there at the moment that supports adoptable storage and a replaceable battery, and no tablet supporting adoptable storage. It's as if they want you to have their products with a very short usefulness lifespan, if not a physical lifespan.

References
https://nelenkov.blogspot.co.uk/2015/06/decrypting-android-m-adopted-storage.html?view=flipcard
http://forum.xda-developers.com/galaxy-tab-s/general/patch-adoptable-storage-enabler-t3460478/page4
http://odindownload.com/
http://www.modaco.com/news/android/heres-how-to-configure-adoptable-storage-on-your-s7-s7-edge-r1632/

Galaxy Tab S stuck in "Downloading" mode

Galaxy Tab S stuck in "Downloading" mode:

Press: Power + Vol Down + Home for 20 seconds

Recovery Mode
Turn off the device
Press and hold Volume UP key + Home Key
then Press and hold Power key
Release all key when you see Android System Recovery
Use Volume UP key and Volume Down key to select Menu
Use Power key to Confirm or Execute Menu

Download Mode or ODIN Mode
Turn off the device
Press and hold Volume Down key + Home key
then Press and hold Power key
Release all key when you see ODIN Mode
Use Volume Up key to continue
Use Volume Down key to cancel ( Restart the device )

Odin is a tool for updating the firmware on Samsung devices.