On a Hunt for the Traces of the Next Step in IT Evolution:
History, Fundamentals & Future of Cloud Native

Benjamin Nothdurft

codecentric AG

Benjamin Nothdurft

benjamin.nothdurft@codecentric.de

twitter.com/dataduke

Agenda

  • Brief History of Cloud Native

  • Drivers for Cloud Native

  • CNA Principles

  • Designing CNA

  • Tooling for CNA

Brief History of Cloud Native

General Observation

  • Over the course of many different scientific publications attributes were defined that applications need to comply to be calling/claiming themselves as cloud native

     – in contrast to classical  distributed systems

2006

  • 13th March: Amazon S3 was launched

    New Terms:

     
  • Public/Private/Hybrid Cloud Computing
  • IaaS (Infrastructure as Code)
  • PaaS (Platform as a Service)
  • SaaS (Software as a Service)

2011


 

  • no mentioning of CNA yet
    even though the terms were already present

2012

  • popularity of CNA was growing in the industry steadily and the usage of term got spread especially in academia
     
  • first draft to Cloud-Native Engineering was presented at a research/computer science conference: contains pattern-based methodology for Cloud Native Application Designs, with systematic identification of consistencies, availability and outage tolerances (CAP-theorem)

2015

  • sudden growth in popularity of the term "cloud native" and big traction in the industry which search trends of Google show.
  • at that time Cloud Native Applications were recognized as the logical evolution of Microservies Architectures which took Containerization into account

2017

2017

Definition after Analysis

A cloud-native application (CNA) is a distributed, elastic

and horizontal scalable system composed of (micro)services

which isolates state in a minimum of stateful components.

The application and each self-contained deployment unit of

that application is designed according to cloud-focused de-

sign patterns and operated on a self-service elastic platform.

considers the IDEAL attributes (2014)

  • I = isolated state
  • D = distributed in its nature
  • E = elastic (horizontale scaling)
  • A = automated management
  • L = loosely coupled

considers assumptions for motivation of CNA architecture application (Kratz, Quint 2014)

  • speed
  • safety
  • scale
  • client diversity

Balalie converted these motivational assumptions in applications- and infrastructure  approaches (2016)

  • Microservices ("doing one thing well")
  • Published and Versioned APIs (HTTP, REST, JSON)
  • Collection of Cloud-Focused Patterns (The Twelve Factor App of Heroku, Circuit Breaker Pattern by Fowler, Cloud Computing Patterns by Fehling and Erlet)
  • Self-Service Agile Infrastructure Platforms (delivery and operations tools as enclosed, container-based units; additional functionality via IaaS, request-based scaling of individual CNAs, monitoring, dynamic routing, load balancing, aggreation of logs and metrics)

Katz and Quint also include other popular definitions
(1/3)

  • Elasticity: workload based (de-)provisioning of systems and resources to every moment to align to the required payload of the business requirements/usage immediately
  • Scalability: structual (align to specific desired dimension) and load-based scaling (steadily changing dimension)
  • Microservices: to be developed architecture style by Fowler, should be based on Business Requirements, Use Domain-Driven Design instead of  technical-driven modelling approaches
  • Self-contained Deployment Unit: technical implementation of units in the application delivery topography. standardized containers play an important role as defined by the  Open Container Initiative (OCI) in "5 Principles for Containers"
  • Stateful Components: will be used to sync different instances  of application components to deliver a uniform behaviour
  • Elastic Platform: is an integreated distribution plattform to run specific applications incl. their communication, data storage and other services (e.g. K8s, Swarm, OpenShift)

Katz and Quint also include other popular definitions
(2/3)

  • DevOps: Build, Test, Run software by automation. Dev and Ops work closely together in cross-functional teams
  • Softwareization Shift: ongoing automation in infrastructure and networks through software solutions
  • Stateisolation: Stateless components can scales easier. statefull components shall be reduced to a minimum and use scalable data store
  • Loose Coupling: service composition by events and data. Event Coupling can be realized via messages (AMQP). Data Coupling is realized by high performant data stores, which often use the concept of eventual consistency.

Katz and Quint also include other popular definitions
(3/3)

Drivers of Cloud Native

The traditional IT “best practices” are counterproductive because they solve
 a completely different problem

What are the new drivers?

What are the new goals?

How should organizations look like?

What are the implications?

CNA Principles

The Twelve Factor App (2012)

...and beyond (2016)

1. One codebase, one application

2. API first

3. Dependency management

4. Design, build, release, and run

5. Configuration, credentials, and code

6. Logs

7. Disposability

8. Backing services

9. Environment parity

10. Administrative processes

11. Port binding

12. Stateless processes

13. Concurrency

14. Telemetry

15. Authentication and authorization

Microservices Patterns (2018)

Chis Richardson

Text

SOLID Principles for Cloud Native Applications (2018) by Red Hat

ISA Principles

Event Storming (2018)

DDD: Strategic Design Tools

DDD: Tactical Design Tools

Designing CNA

Domain Events

product added to cart
  • domain event (past tense)

  • orange sticky note

  • relevant for domain experts

Command-Event-Pairs

  • command (present tense)

  • blue sticky note

  • triggers a domain event

product add to cart

Command-Event-Pairs

  • command (present tense)

  • blue sticky note

  • triggers a domain event

product add to cart

Aggregates / Entities

  • aggregate/entity

  • yellow sticky note

  • data that is interacted with

product

Borders & Flow

  • find borders where a domain model has a different meaning

  • domain events flow between different models

Bounded Contexts

  • bounded context/sub-domain

  • red sticky note

  • core name that is relevant/valid

checkout

Outcome / Big Picture

  • narrative/stories/user journey

  • line(s)

  • multiple storytellings

  • common language for everyone

provider-ui

storefront-ui

merchant-ui

Frontend

api proxy

tenant

auth

Core

business unit

site

shop

Shop Admin

Product

product

. . .

. . .

. . .

. . .

Bounded Contexts > Microservices

Context Map Patterns Categorized

Model Flow

 Upstream Patterns

  • Open Host Service

  • Event Publisher

 Downstream Patterns

  • Customer / Supplier

  • Conformist

  • Anticorruption Layer

 In-Between Patterns

  • Shared Kernel

  • Published Language

  • Seperate Ways

Upstream System
Down-stream System

Open Host Service

  • Bounded Context offers defined set of services that expose functionality

  • Any downstream system can implement their own integration

  • Especially useful with many systems that want to integrate

SOAP/REST
Upstream System
Down-stream System

Open Host Service

@RepositoryRestController
@RequiredArgsConstructor
public class SiteCustomController {

    @NonNull
    private final SiteService siteService;
    @NonNull
    private final EntityLinks entityLinks;

    @PostMapping("sites")
    public ResponseEntity<Resource<Site>> create(@Valid @RequestBody SiteWithAdminUser body) {
        SiteCreateResource siteBody = body.getSite();

        Site transientSite = Site.builder()
                .active(siteBody.isActive())
                .hostname(siteBody.getHostname())
                .locale(siteBody.getLocale())
                .build();
        Site site = siteService.create(transientSite, body.getAdminUser());

        return ResponseEntity
                .created(entityLinks.linkForSingleResource(Site.class, site.getId()).toUri())
                .body(new Resource<>(site));
    }

    @DeleteMapping("sites/{siteId}")
    public ResponseEntity<Void> delete(@PathVariable("siteId") Optional<Site> site) {

        site.ifPresent(siteService::delete);
        return ResponseEntity.noContent().build();
    }
}

Event Publisher

  • Bounded Context published Domain Events

  • Example: Messaging or Feeds

  • Other bounded context may subscribe to those events to react upon them.

Message
Upstream System
Down-stream System

Event Publisher

@Component
@Slf4j
@RequiredArgsConstructor
public class MailMessagePublisher {

    private final DomainMessagePublisher domainMessagePublisher;
    private final DomainMessageFactory domainMessageFactory;

    public void publishMailSentEvent(ClientResponse response) {
        publishMessage("mailgateway.mail.sent.event", response);
    }

    private void publishMessage(String messageType, ClientResponse response) {
        MailSendEventMessage mail = MailSendEventMessage.builder()
                .messageId(response.getId())
                .status(QUEUED)
                .build();
        DomainMessage<MailSendEventMessage> message = domainMessageFactory.create(messageType, mail);
        domainMessagePublisher.publish(message);
        log.info("Published message of type {}", message.getMessageType());
    }

    @AllArgsConstructor
    @Builder
    @Getter
    @JsonTypeName("mail-sent-event")
    static class MailSendEventMessage implements DomainMessagePayload {

        private String messageId;
        private MailStatus status;
    }
}

Event Publisher

Event Publisher

@Slf4j
@MessagePublisher
@RequiredArgsConstructor
class ImageMessagePublisher(
        private val domainMessageFactory: EntityDomainMessageFactory,
        private val domainMessagePublisher: DomainMessagePublisher,
        private val storageApiUriBuilder: StorageApiUriBuilder
) {
    val log: Logger = LoggerFactory.getLogger(javaClass)

    fun publishCreatedEvent(image: Image) {
        publishMessageWithoutChangeSet(image, "shop.image.created.event")
    }

    fun publishDeletedEvent(image: Image) {
        publishMessageWithoutChangeSet(image, "shop.image.deleted.event")
    }

    private fun publishMessageWithoutChangeSet(image: Image, messageType: String) {

        val message = domainMessageFactory
                .prepareDomainMessageForType(messageType)
                .withEntity(image)
                .withPayloadType("image")
                .withCurrentTenant()
                .withoutChangeSet()
                .withAdditionalProperty("dataUri", toRelativeUri(image))
                .build()
        log.info("publishing message {}", message)
        domainMessagePublisher.publish(message)
    }
    private fun toRelativeUri(image: Image?): String? =
            image?.let { storageApiUriBuilder.getRelativeImageDownloadUri(it.dataUri).expand().toString() }
}

Customer / Supplier

  • Two teams share a customer / supplier relation

  • The downstream team is considered to be the customer

  • Veto rights may be applicable

Veto
Upstream System
Down-stream System

Customer / Supplier

@Test
@PactVerification()
public void givenGet_whenSendRequest_shouldReturn200WithProperHeaderAndBody() {
  
    // when
    ResponseEntity<String> response = new RestTemplate()
      .getForEntity(mockProvider.getUrl() + "/pact", String.class);
 
    // then
    assertThat(response.getStatusCode().value()).isEqualTo(200);
    assertThat(response.getHeaders().get("Content-Type").contains("application/json")).isTrue();
    assertThat(response.getBody()).contains("condition", "true", "name", "tom");
}

Consumer-Driven Contracts with Pact

Conformist

  • The downstream conforms to the model of the upstream team

  • No translation of models

  • No veto rights

Upstream System
Down-stream System
@RunWith(SpringRunner.class)
@ShopApplicationTest
public class SiteEventContractTest {

    @Autowired
    private MessageStubTrigger messageStubTrigger;
    @MockBean
    private SiteEventSubscriber siteEventSubscriber;
    @Captor
    private ArgumentCaptor<DomainMessage<SitePayload>> siteEventCaptor;
    @Captor
    private ArgumentCaptor<DomainMessage<SiteFeaturesPayload>> siteFeaturesEventCaptor;

    @Test
    public void should_process_site_created_event()  {
        messageStubTrigger.trigger("site.site.created.event");
        thenVerify(siteEventSubscriber).should().consumeSiteCreatedEvent(siteEventCaptor.capture());
        then(siteEventCaptor.getValue().getTenantId()).isNotNull();
        then(siteEventCaptor.getValue().getPayload().getLocale()).isNotNull();
    }

    @Test
    public void should_process_site_deleted_event()  {
        messageStubTrigger.trigger("site.site.deleted.event");
        thenVerify(siteEventSubscriber).should().consumeSiteDeletedEvent(siteEventCaptor.capture());
        then(siteEventCaptor.getValue().getTenantId()).isNotNull();
    }

    @Test
    public void should_process_site_features_updated_event()  {
        messageStubTrigger.trigger("site.site-features.updated.event");
        thenVerify(siteEventSubscriber).should().consumeSiteFeaturesUpdatedEvent(siteFeaturesEventCaptor.capture());
        then(siteFeaturesEventCaptor.getValue().getTenantId()).isNotNull();
        then(siteFeaturesEventCaptor.getValue().getPayload().getFeatures()).isNotEmpty();
    }
}

Conformist

Anticorruption Layer

  • A layer that isolates a client model from the provided model by translation

  • external vs. internal model

Upstream System
Down-stream System
public enum GoogleAvailability {

    @JsonProperty(value = "in stock")
    IN_STOCK("in stock"),
    @JsonProperty(value = "out of stock")
    OUT_OF_STOCK("out of stock"),
    @JsonProperty(value = "preorder")
    PRE_ORDER("preorder");

    private String name;

    GoogleAvailability(String name) {
        this.name = name;
    }

    public static GoogleAvailability fromAvailabilityState(AvailabilityState availability) {
        if (availability == null
                || availability.equals(AvailabilityState.IN_STOCK)
                || availability.equals(AvailabilityState.LOW_STOCK)) {
            return IN_STOCK;
        } else if (availability.equals(AvailabilityState.OUT_OF_STOCK)
                || availability.equals(AvailabilityState.NOT_AVAILABLE)) {
            return OUT_OF_STOCK;
        } else {
            return PRE_ORDER;
        }
    }
}

Anticorruption Layer

Shared Kernel

  • Two team share a (subset of the) domain model

  • can be shared lib or a database

Upstream System
Down-stream System
public class DomainMessage<T extends DomainMessagePayload> {

    private String messageType;

    private LocalDateTime timestamp;

    private Integer tenantId;

    @JsonInclude(NON_EMPTY)
    private Collection<DiffItem> changeSet = emptyList();

    private T payload;

    private DomainMessage() {
        // needed by Jackson
    }

    @NotNull
    public LocalDateTime getTimestamp() {
        return timestamp;
    }

    @Nullable
    public Integer getTenantId() {
        return tenantId;
    }

    @NotNull
    public String getMessageType() {
        return messageType;
    }

    @NotNull
    public Collection<DiffItem> getChangeSet() {
        return changeSet;
    }

    @NotNull
    public T getPayload() {
        return payload;
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper(this) //
                .add("messageType", messageType) //
                .add("timestamp", timestamp) //
                .add("tenantId", tenantId) //
                .add("changeSet", changeSet) //
                .add("payload", payload) //
                .toString();
    }

    @NotNull
    public static <T extends DomainMessagePayload> DomainMessageBuilder<T> builder(@NotNull String messageType) {
        return new DomainMessageBuilder<>(messageType);
    }

  
    @JsonIgnore
    public boolean isChangeSetRelevantForPayload() {
        Set<String> possiblePathNames = Arrays.stream(BeanUtils.getPropertyDescriptors(payload.getClass()))
                .map(PropertyDescriptor::getName)
                .map(property -> "/" + property)
                .collect(toSet());
        return changeSet.stream()
                .map(DiffItem::getPath)
                .anyMatch(possiblePathNames::contains);
    }

    public static class DomainMessageBuilder<T extends DomainMessagePayload> {

        private final String messageType;

        private Integer tenantId;

        private LocalDateTime timestamp = LocalDateTime.now();

        private Collection<DiffItem> changeSet = emptyList();

        @NotNull
        private DomainMessageBuilder(@NotNull String messageType) {
            this.messageType = checkNotNull(messageType);
        }

        @NotNull
        public DomainMessageBuilder<T> timestamp(@NotNull LocalDateTime timestamp) {
            this.timestamp = checkNotNull(timestamp);
            return this;
        }

        @NotNull
        public DomainMessageBuilder<T> tenantId(@Nullable Integer tenantId) {
            this.tenantId = tenantId;
            return this;
        }

        @NotNull
        public DomainMessageBuilder<T> changeSet(@Nullable Collection<DiffItem> changeSet) {
            this.changeSet = changeSet;
            return this;
        }

        @NotNull
        public DomainMessage<T> build(@NotNull T payload) {
            requireJsonTypeInformation(checkNotNull(payload).getClass());
            final DomainMessage<T> domainMessage = new DomainMessage<>();
            domainMessage.messageType = this.messageType;
            domainMessage.timestamp = this.timestamp;
            domainMessage.tenantId = this.tenantId;
            domainMessage.payload = payload;
            domainMessage.changeSet = changeSet;
            return domainMessage;
        }
    }
}

Shared Kernel


dependencyManagement {
    imports {
        mavenBom "com.example.demo:shared-parent:1.2.3"
    }
}

dependencies {
    compile     ("com.example.demo:shared-message")
}

Shared Kernel

Published Language

@NoArgsConstructor(access = PRIVATE)
@AllArgsConstructor(access = PRIVATE)
@EqualsAndHashCode(callSuper = false)
@Getter
@Builder
@Embeddable
public class Identifier implements Serializable {

    private static final long serialVersionUID = -123242542L;

    @Enumerated(STRING)
    @NotNull
    @Column(name = "TYPE", length = 10, nullable = false)
    private IdentifierType type;

    @NotNull
    @Column(name = "VALUE", length = 255, nullable = false)
    private String value;
}


public enum IdentifierType {
    EAN,
    UPC,
    ISBN,
    MPN
}

Seperate Ways

  • No connection between bounded contexts exists

  • Teams can find their own solution for their domain

Upstream System
Down-stream System

Tooling for CNA

Deployment: CI/CD-Pipeline

In 2015 the Cloud Native Computing Foundation
(CNCF,
www.cncf.io) was founded under the umbrella of the Linux Foundation with companies like

Cloud Foundry, Core OS, Docker, Google, Mesosphere, Red Hat, Twitter, VMWare oder Weaveworks
and over 100.000 members.

Goal: Standardization of existing technologies and cultivate a culture of open source to simplify starting with cloud native

Agreed Definition by CNCF Members

Cloud native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. Containers, service meshes, microservices, immutable infrastructure, and declarative APIs exemplify this approach.

These techniques enable loosely coupled systems that are resilient, manageable, and observable. Combined with robust automation, they allow engineers to make high-impact changes frequently and predictably with minimal toil.

The Cloud Native Computing Foundation seeks to drive adoption of this paradigm by fostering and sustaining an ecosystem of open source, vendor-neutral projects. We democratize state-of-the-art patterns to make these innovations accessible for everyone.

 Modern Definitions

by Tool Companies

Cloud Native Trailmap

Cloud Native Landscape

Graduated Projects

Incubating Projects

Future & Wrap-Up

Business and IT today are the same side of the same coin.
The other side is the market.

General Recommendation

  • Why is software eating the world? (Marc Andreessen)
    > Not true! Not every company needs to become a software company
     
  • CNA should still be always observed to not be hit by digital, disruptive business models and go bankrupt like Uber, AirBnB, Netflix
     
  • market leaders provide open source solution for going into the cloud -> use them (e.g. NetflixxOSS released in 2012 to the public)
  • Markets have changed
  • IT has changed

  • The role of IT has changed

  • New drivers

  • New goals

  • New building blocks

  • DevOps results in a re-org of IT

  • Dev(Sec)Ops drives implementation of a new IT

Sources

Slides

Benjamin Nothdurft

benjamin.nothdurft@codecentric.de

twitter.com/dataduke

3 Core Topics for Companies

  • Speed: Firmen sollen schnell an Apps arbeiten können, ohne sich lange mit der Infrastruktur, Integration oder einhergehenden Experimenten und Bugs aufhalten müssen.
  • Freedom: Jede App soll basierend auf Open Source Packaging überall laufen können, um einen Cloud Service Vendor Lock-In zu vermeiden
  • Trust: Firmen müssen auf eine Uptime von 24/7 mit hoher Wiederherstellungsrate (Recovery Time) vertrauen können, wobei jede Komponente unabhängig skalierbar ist.

Copy of Cloud-Native

By Benjamin Nothdurft

Copy of Cloud-Native

On a Hunt for the Traces of the Next Step in IT Evolution: History, Fundamentals and Future of Cloud Native - https://www.meetup.com/de-DE/codecentric-Dortmund-tech-talk/events/268080863/

  • 470