Skip to main content
Ben Nadel at CFCamp 2023 (Freising, Germany) with: Luis Majano and Jürg Anderegg
Ben Nadel at CFCamp 2023 (Freising, Germany) with: Luis Majano ( @lmajano ) Jürg Anderegg

The Beauty Of Modular Code: Adding Different Transports To StatsDGateway

By on
Tags:

Earlier this week, I released StatsDGateway, which is a set of ColdFusion components that facilitate the sending of messages from ColdFusion to a statsD server. When I first wrote the library, it was a single component. However, after watching Corey Haines presentation on Simple Design, I went back and refactored the single component into multiple components that each have a much more focused responsibility. The beauty of this is that I can now easily swap in-and-out different implementations to achieve different behaviors. And, this is exactly what I've done with the transport implementations.

View the StatsDGatway.cfc project on my GitHub account.

When you construct a StatsDClient.cfc instance manually (ie, not using the factory in the StatsDGateway.cfc), you have to use constructor-injection to provide the client with a transportation implementation that adheres to the following interface:

  • sendMessage( message )
  • destroy()
  • isDestroyed()

The actual mechanism of the message sending is totally hidden and left up to the given transport implementation. As such, I can easily change the type of transportation to use UDP or HTTP or even to prevent message-sending altogether and just keep the messages in an internal queue - CaptureTransport.cfc (which is great for unit testing).

The trasport components end up being composable as well. So, I can wrap one transport, such as UDPTransport.cfc, inside of another transport like BufferedTransport.cfc, that will buffer messages until they reach a certain size and then send them all as one concatenated string.

<cfscript>

	// Create a statsD client that uses a transient UDP socket (one that is created and
	// closed for each message, which prevents the need for explicit clean-up).
	client = new lib.client.StatsDClient(
		new lib.transport.UDPTransport(),
		new lib.sampler.RandomSampler()
	);

	// Create a statsD client that uses a persistent UDP socket (one that will remain
	// open for the lifetime of the statsD client, until .destroy() is called).
	client = new lib.client.StatsDClient(
		new lib.transport.PersistentUDPTransport(),
		new lib.sampler.RandomSampler()
	);

	// Create a statsD client that will buffer messages until they reach a max length
	// of 500 characters, at which point they will sent out as a \n-delimited list.
	client = new lib.client.StatsDClient(
		new lib.transport.BufferedTransport(
			new lib.transport.UDPTransport(),
			500
		),
		new lib.sampler.RandomSampler()
	);

	// ---

	// Create a statsD client that doesn't actually send messages to statsD. Instead,
	// the messages will be captured in an internal queue that can be used to test the
	// throughput of the statsD client.
	transport = new lib.transport.CaptureTransport();

	client = new lib.client.StatsDClient(
		transport,
		new lib.sampler.RandomSampler()
	);

	// ... send messages.

	// Check to see that the client actually sent messages.
	writeDump( trasnport.getSentMessages() );

</cfscript>

This might seem really obvious to mature programmers; but, I have never felt very confident in my object design. So for me, breaking a component up and then seeing how much the modularity facilitates extending the functionality is an extremely rewarding process. For the first time in my life, I think I'm starting to understand the Single Responsibility Principle, which is of course, one of Uncle Bob Martin's SOLID principles.

Want to use code from this post? Check out the license.

Reader Comments

8 Comments

Hi Ben,

Sorry for the pedantry but should 'trasports' be 'transports'?
I am rubbish at spelling though, so I'm probably wrong, or misreading.

Adam

15,663 Comments

@Adam,

Ugg, you are totally right :( Sometimes, I am baffled at how my spellchecker doesn't catch these things... or how my brain selectively ignores them :D

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel