DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Event-Driven Order Processing Program
  • Continuous Testing in the Era of Microservices and Serverless Architectures
  • Leveraging Datadog and Solace PubSub+ for Improved Visibility in Event-Driven Systems
  • Distributed Tracing: A Full Guide

Trending

  • How to Merge HTML Documents in Java
  • Understanding the Shift: Why Companies Are Migrating From MongoDB to Aerospike Database?
  • Supervised Fine-Tuning (SFT) on VLMs: From Pre-trained Checkpoints To Tuned Models
  • The Role of AI in Identity and Access Management for Organizations
  1. DZone
  2. Data Engineering
  3. Data
  4. Event-Driven Hello World Program

Event-Driven Hello World Program

This article shows the outline of creating and testing a simple microservice, which provides the basis for microservices that are easy to deploy and maintain.

By 
Peter Lawrey user avatar
Peter Lawrey
·
Jun. 08, 22 · Analysis
Likes (11)
Comment
Save
Tweet
Share
7.0K Views

Join the DZone community and get the full member experience.

Join For Free

Event-driven microservices can be straightforward to describe before implementing, testing, and maintaining. They are also highly responsive to new information in real time, with latencies in Java of below 10 microseconds 99.99% of the time, depending on the functionality of the small, independently deployable microservice. 

In this introductory article, we use an example event-driven Hello World program (a programming paradigm where the program flow is determined by events) to step through behaviour-driven development, where we describe the behaviour the business needs first as test data and writing a very simple microservice which turns input events like this:


say: Hello World 


Into outputs like this, by adding an exclamation point:


say: Hello World!  # <- adds an exclamation point 


All the code for this example is available on GitHub.

When modeling Event-Driven systems, a useful pattern is to have event-driven core systems with gateways connecting to external systems that might not be event-driven. To keep a clear separation of concern, business logic such as making a decision based on market data, or processing an order, is placed in the event-driven microservices, as these are the easiest to test, with the gateways connecting to external clients and systems being as thin as possible, so they are only concerned with acting as adapters and avoid containing significant business logic. 

Domain-Driven Design is a focus on determining the requirements of domain experts. Their requirements are further divided into event-driven microservices. Where the information is passed as a series of events between the microservices.

The requirements for each internal microservices can be described in YAML for Behaviour-Driven Development.

All examples are in the Chronicle-Queue-Demo/hello-world module.

A Simple Event-Driven Contract

We model events as asynchronous method calls without arguments or one-to-many arguments e.g.

 
public interface Says {

   void say(String words);

}

This is the simplest Hello World example to get started. We can add to this interface other event types (methods) with multiple parameters. Parameters don’t have to be just primitives; they can also be complex data structures such as Data Transfer Objects.

There is no assumption about how the events produced by the microservice will be processed. It might be recorded but otherwise ignored, for now, processed immediately by a single microservice or read by multiple downstream microservices sometime later.  Thus, it doesn’t return a value. Any results will be emitted as events from the respective event handlers. In programming, an event handler is a callback routine that can operate asynchronously.

External Event Producers and Consumers

Often we need to integrate with the client’s external systems. As this is a simple “Hello World” example, let’s imagine that instead of external systems connected via gateways, we have a simple program that reads input from the console to provide upstream events and another simple program to write to the console acting as a downstream gateway.

 
public class SaysInput {

   public static void input(Says says) throws IOException {

       BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

       for (String line; ((line = br.readLine()) != null); )

           says.say(line);

   }

}

public class SaysOutput implements Says {

   public void say(String words) {

       System.out.println(words);

   }

}


These can be integrated easily as the output of one is wired to the input of the other.


 
public class RecordInputToConsoleMain {

   public static void main(String[] args) throws IOException {

       // Writes text in each call to say(line) to the console

       final Says says = new SaysOutput();

       // Takes each line input and calls say(line) each time

       SaysInput.input(says);

   }

}


We can also record everything the producer performs to YAML to build tests later.


 
public class RecordInputAsYamlMain {

   public static void main(String[] args) throws IOException {

       // obtains a proxy that writes to the PrintStream the method calls and their  arguments

       final Says says = Wires.recordAsYaml(Says.class, System.out);

       // Takes each line input and calls say(theLine) each time

       SaysInput.input(says);

   }

}



Use the following to replay the output from a file.

 
public class ReplayOutputMain {

   public static void main(String[] args) throws IOException {

    // Reads the content of a Yaml file specified in args[0] and feeds it to SaysOutput.

     Wires.replay(args[0], new SaysOutput());

   }

}


Unit Tests for the RecordAsYaml and Replay Methods

To test the functionality of recordAsYaml and replay methods in isolation and verify if they work as suggested above, the following unit tests were developed.  Having lots of text in unit tests is cumbersome, and in the next section, you can see how this text can be taken from files.


 
public class WiresTest extends WireTestCommon {



@Test

public void recordAsYaml() {

   ByteArrayOutputStream baos = new ByteArrayOutputStream();

   PrintStream ps = new PrintStream(baos);

   Says says = Wires.recordAsYaml(Says.class, ps);

   says.say("One");

   says.say("Two");

   says.say("Three");



   assertEquals("" +

           "---\n" +

           "say: One\n" +

           "...\n" +

           "---\n" +

           "say: Two\n" +

           "...\n" +

           "---\n" +

           "say: Three\n" +

           "...\n",

           new String(baos.toByteArray(), StandardCharsets.ISO_8859_1));

}



@Test

public void replay() throws IOException {

   ByteArrayOutputStream baos = new ByteArrayOutputStream();

   PrintStream ps = new PrintStream(baos);

   Says says = Wires.recordAsYaml(Says.class, ps);

   says.say("zero");

   Wires.replay("=" +

           "---\n" +

           "say: One\n" +

           "...\n" +

           "---\n" +

           "say: Two\n" +

           "...\n" +

           "---\n" +

           "say: Three\n" +

           "...\n",says);



   assertEquals("" +

           "---\n" +

           "say: zero\n" +

           "...\n" +

           "---\n" +

           "say: One\n" +

           "...\n" +

           "---\n" +

           "say: Two\n" +

           "...\n" +

           "---\n" +

           "say: Three\n" +

           "...\n", new String(baos.toByteArray(), StandardCharsets.ISO_8859_1));

}



interface Says {

   void say(String word);

}

}


By recording and replaying using YAML, our microservices are written, tested, and debugged easily without any involvement of the messaging layer.

Let’s add a microservice as a data processor as a class that can have one or more event types. This microservice gets input events as text messages and adds an exclamation mark to them, and relays them to the output gateway.


 
public class AddsExclamation implements Says {

   private final Says out;



   public AddsExclamation(Says out) {

       this.out = out;

   }



   public void say(String words) {

       this.out.say(words + "!");

   }

}


A Single-Threaded Event-Driven Process

We can combine these all stages in one process, one thread. While this is unlikely to be useful in production, putting microservices into a single thread makes it easier to test and debug.


 
public class DirectWithExclamationMain {

   public static void main(String[] args) throws IOException {

       SaysInput.input(new AddsExclamation(new SaysOutput()));

   }

}


Testing a Single Event-Driven Service

Instead of embedding large amounts of text in a test, we can read resource files. This makes them easier to read and maintain.

 
public class AddsExclamationTest {

   @Test

   public void say() throws IOException {

YamlTester yt = YamlTester.runTest(AddsExclamation.class, "says");

assertEquals(yt.expected(), yt.actual());

   }

}




Let’s update the input to see how easy it is to maintain this test. I will change the second input to Hello World and run the test again.

 
src/test/resources/says/in.yaml
---

say: One

...

---

say: Hello World

...

---

say: Three

...


Not only does the test fail, but I can also on the differences to see clearly why.

At this point, I can either fix the test or I can accept the change by copying and pasting the actual result over the expected result in the out.yaml file.

In the next post, we will see how to implement a more realistic example of processing orders and automate many microservice tests from the configuration.  This provides a basis for creating highly performant, deterministic, redundant microservices.

This article shows the outline of creating and testing a simple microservice, which provides the basis for microservices which are easy to deploy and maintain.

Event microservice Testing

Opinions expressed by DZone contributors are their own.

Related

  • Event-Driven Order Processing Program
  • Continuous Testing in the Era of Microservices and Serverless Architectures
  • Leveraging Datadog and Solace PubSub+ for Improved Visibility in Event-Driven Systems
  • Distributed Tracing: A Full Guide

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!