Slide 1

Slide 1 text

MicroServices MicroServices A Developer’s Point of View

Slide 2

Slide 2 text

Who we are Content Acquisition: ~3800 Journals ~350,000 Accepted submissions for year New Peer Review system New Rights and Payments collection

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Story Mapping

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

Kanban and Daily Standup ● Physical board for tracking dev and qa ● Jira for Managers and Business Analysts

Slide 7

Slide 7 text

Architectural Decision Record

Slide 8

Slide 8 text

Platform as a Service

Slide 9

Slide 9 text

CI/CD Pipelines Automated ● Each commit can potentially recreate all pipelines ● No need to manually configure CI server ● Each MicroService declare its dependencies ● Our in house solution is not perfect but quite good

Slide 10

Slide 10 text

Trunk Based Development Pairing and TDD ● Pairing enforce good habits ● No code review and pull requests ● Tripling and Mobbing as well ● Remote pairing with our team in India ● Slack channels for pairs comunications ● Whiteboard discussions all the time

Slide 11

Slide 11 text

Kotlin and HTTP4K

Slide 12

Slide 12 text

Microservices Architecture

Slide 13

Slide 13 text

fun main() { Bootstrap().start(AlpacaCreator) } object AlpacaCreator : ApplicationCreator { override fun invoke(bootstrap: Bootstrap): Application { val monitor = LoggingMonitor(bootstrap.logging) val eventStore = PgEventStore( DatabaseTransactor(bootstrap.configuration), monitor ) val sparkPostClient = SparkPostClient( bootstrap.createHttp4kClient(SparkPost), bootstrap.configuration[OperationalKeys.SPARKPOST_SECURITY_TOKEN] ) val emailNotifier = EmailNotifier( sparkPostClient, monitor ) val appHandler = Alpaca( CommandHandler(eventStore, emailNotifier), DashboardProjection(eventStore)) ) return Application( bootstrap.createHttp4kServer(appHandler)) } From Adapters From Domain

Slide 14

Slide 14 text

Mono Repo ● Single Git repository ● Split in modules with source dependency – Underware: useful snippets, no dependencies – Domain(s): depend only on underware – Adapters: depend on domain but not on other adapters – Executables: connecting a domain with one or more adapters, almost no code

Slide 15

Slide 15 text

Separated pipelines ● No artifact repository ● No versioning, no SNAPSHOT jars ● Independent release of microservices ● Code reuse, no frameworks ● Easy to refactor, change and test ● Working on insulating better Bounded Contexts

Slide 16

Slide 16 text

Monitoring Logging ● ELK (the usual sospect) ● Tracing Monitor Zipkin (TraceId + SpanId) ● Dedicated Monitor Events (No Log4j or similar, no log levels) ● As little as possible when things work As much as possible when there is an error

Slide 17

Slide 17 text

Testing ● Unit tests, TDD ● External Integration Tests ● Database Migration Tests ● UI Automation Tests ● Consumer Driven Contract Tests ● Domain Driven Tests

Slide 18

Slide 18 text

Consumer Driven Contract Testing ● Module A declares that it consumes B api ● A CDC test in A verify that A works with a B in- memory implementation ● When B change, it runs all cdc tests from all modules that are B consumers

Slide 19

Slide 19 text

abstract class ProductionSystemContract { abstract val productionClient: ProductionSystem abstract val validSubmissionId: AcceptedSubmissionId @Rule @JvmField val allowRunInEnvironments = AllowRunInEnvironments @Test @RunInEnvironments(local, test, staging) fun `Releases an accepted submission in the production workflow`() { productionClient.releaseForPublication(validSubmissionId).expectSuccess() } @Test @RunInEnvironments(local, test, staging) fun `Errors for an invalid accepted submission`() { productionClient.releaseForPublication(AcceptedSubmissionId("unknown")).expectFailure() }} … interface ProductionSystem { fun releaseForPublication(id: AcceptedSubmissionId): Result }

Slide 20

Slide 20 text

Domain Driven Testing ● A test to describe a use case from a scenario ● The personas in the test are Roles interfaces ● Roles have an implementations which use adapters and Http/Db/Files etc. ● Roles have another implementation which use only domain objects ● Tests verify that the logic is correct and that there is no business logic in the adapter layer

Slide 21

Slide 21 text

class HandleAcceptedSubmissionManuallyTest { @Rule @JvmField val scenario = theApplication.newScenario() val peterProdEditor by lazy{ scenario.newProductionEditor() } val aliceTheAuthor by lazy{ scenario.newAuthor(aliceDetails, aliceId) } @WorkInProgress(until = "2019-04-15", by = "UB") @Test fun `Oasis processes a manuscript and releases it to production`() { peterProdEditor.`create a new author approval from a accepted submission`(acceptedSubmission) aliceTheAuthor.`has received an email containing the links to pdf forms to compile`(acceptedSubmission.id) peterProdEditor.`after receiving the email with a licence he can release it to production`(acceptedSubm ssion.id, LicenceType.OPEN_ACCESS) peterProdEditor.`see the submission returned on production`(acceptedSubmission.id) } } interface ProductionEditorRole { fun `after receiving the email with a licence he can release it to production`(acceptedSubmissionId… fun `see the submission returned on production`(acceptedSubmissionId: AcceptedSubmissionId) fun `create a new author approval from a accepted submission`(acceptedSubmission: AcceptedSubmission) }

Slide 22

Slide 22 text

QA Exploratory Testing

Slide 23

Slide 23 text

Ask Me Anything And follow me on Twitter and Medium @ramtop