Designing testable serverless apps using hexagonal architecture

Designing testable serverless apps using hexagonal architecture

Presentation from Serverless Days Hamburg 2019 (https://hamburg.serverlessdays.io).

Description:

The main idea of hexagonal architecture is to allow an application to equally be driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases. On the other side, it is not trivial to run and test serverless applications locally. But what if we apply hexagonal architecture to serverless functions?

It seems they are natural fit!

With hexagonal architecture, your can design beautiful serverless functions, that can be run and tested locally or in the serverless environment. It also allows you to write unit tests without complex mocks, and as many integration tests as you need. As a bonus, your code looks clean and it’s easy to maintain, and hexagonal architecture helps you fighting vendor lock-in.

The main goal of this talk is to show you how to design testable serverless functions. It starts with explanation of hexagonal architecture and it’s importance for serverless apps, and it ends with real-world examples using Node.js.

2663a9ac90cc6ed420e0e1560db57782?s=128

Slobodan Stojanović

February 15, 2019
Tweet

Transcript

  1. 14.

    @slobodan_ "In economics, vendor lock-in, makes a customer dependent on

    a vendor for products and services, unable to use another vendor without substantial switching costs."
  2. 38.

    @slobodan_ "My train of thought went like this: the term

    “lock-in” is misleading. We are really talking about switching costs." Mark Schwartz
 Enterprise Strategist at AWS
  3. 39.

    @slobodan_ "As soon as you commit yourself to a platform

    or a vendor you will have switching costs if you later decide to change." Mark Schwartz
 Enterprise Strategist at AWS
  4. 42.

    @slobodan_ • Planning and analysis • Good architecture • Deployment

    procedures How likely will I need to switch? What would be the cost?
  5. 46.

    Slobodan Stojanovic CTO @ Cloud Horizon & CTO @ Vacation

    Tracker co-author of Serverless Applications with Node.js book AWS Serverless Hero @slobodan_ serverless.pub
  6. 49.

    @slobodan_ Most of the time serverless apps are not fully

    isolated monoliths without integrations
  7. 59.

    @slobodan_ Integration tests are cheaper, but also more important, because

    the common serverless app is split into many small pieces
  8. 62.
  9. 64.

    @slobodan_ Not to another cloud vendor, but to your new

    service, new or changed integration…
  10. 69.

    @slobodan_ "Allow an application to equally be driven by users,

    programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases." Alistair Cockburn
 Creator of Hexagonal architecture
  11. 77.

    @slobodan_ const {
 httpResponse, parseApiEvent,
 SnsRepository
 } = require('../common')
 const

    main = require('./main')
 async function handler(event) { const { body, headers } = parseApiEvent(event) // Create instance of SNS notification repository
 const notification = new SnsRepository(
 process.env.topic ) // Invoke main function with all dependencies
 await main(body, headers, notification)
 return httpResponse()
 }
  12. 93.

    @slobodan_ • Serverless prototype • Small team (1 fulltime developer)

    • Initial product was Serverless chatbot + Express.js and MongoDB • Growing fast (200+ teams using it)
  13. 100.

    @slobodan_ describe('DynamoDB repository', () => { describe('unit', () => {

    ... }) describe('integration', () => { beforeAll(() => { // Create test DB }) afterAll(() => { // Destroy test DB }) // Tests }) })
  14. 101.

    @slobodan_ beforeAll(async () => { const params = { ...

    } await dynamoDb.createTable(params).promise() await dynamoDb.waitFor('tableExists', { TableName: tableName }).promise() })
  15. 102.

    @slobodan_ afterAll(async () => { await dynamoDb.deleteTable({ TableName: tableName }).promise()

    await dynamoDb.waitFor('tableNotExists', { TableName: tableName }).promise() })
  16. 106.

    @slobodan_ What should we do with things that can't be

    tested? For example, Google or someone else deprecated an API while your app is in production
  17. 109.
  18. 110.

    @slobodan_ Serverless apps often heavily relies on front end, in

    those cases you need to track front end errors as well
  19. 112.
  20. 113.
  21. 115.

    @slobodan_ • Good architecture helps you to maintain your switching

    costs low (or at least reasonable) • Hexagonal architecture is a nice fit for serverless apps • Test your integrations (and app in general) • Testing is not enough, you'll need monitoring and error tracking for your serverless apps
  22. 117.

    @slobodan_ • Mark Schwartz - Switching Costs and Lock-In: amzn.to/2S4iT3G

    • Hexagonal Architecture: bit.ly/hex-arch • Vacation Tracker: vacationtracker.io • Hexagonal Architecture at Vacation Tracker: bit.ly/vt-hex-arch • Desole (error-tracking tool): desole.io
  23. 119.