R4 and R5. Correctness and Additional work

Dockerfile to construct every microservice and bring it to production

cloudbanking-users (core)
FROM rust:1.47.0 as builder


WORKDIR /rust


RUN mkdir -p /rust/app/
WORKDIR     /rust/app

COPY ./Cargo.toml ./Cargo.lock ./
COPY ./.env ./
COPY src ./src
RUN cargo build --release --manifest-path=./Cargo.toml --bin cloudbanking


FROM rust:1.47.0 as runner
COPY  --from=builder /rust/app/target/release/cloudbanking /app/core
COPY  --from=builder /rust/app/.env ./app
EXPOSE 3030 3031 3032 3033

WORKDIR /app
CMD ["./core"]
cloudbanking-account
FROM rust:1.47.0 as builder


WORKDIR /rust


RUN mkdir -p /rust/app/account/src/bankaccount_module
WORKDIR     /rust/app

COPY ./account/Cargo.toml ./account
COPY ./account/.env ./account
COPY ./account/src ./account/src
COPY ./account/src/bankaccount_module ./account/src/bankaccount_module
RUN cargo build --release --manifest-path=./account/Cargo.toml --bin account


FROM rust:1.47.0 as runner
COPY  --from=builder /rust/app/account/target/release/account /app/account
COPY  --from=builder /rust/app/account/.env ./app
EXPOSE 3030 3031 3032 3033

WORKDIR /app
CMD ["./account"]
cloudbanking-cards
FROM nimlang/nim:onbuild

COPY ./ ./
CMD nimble build

ENTRYPOINT ["./bin/jester_cards"]
cloudbanking-funds
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3
WORKDIR /work/
RUN chown 1001 /work \
    && chmod "g+rwX" /work \
    && chown 1001:root /work
COPY --chown=1001:root target/*-runner /work/application

EXPOSE 8080
USER 1001

CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

Multi-container production

version: "3"
services:
  # Main Microservice. That expose /api
  cloudbanking-core:
    # Name of container to create
    container_name: cloudbanking-core
    # The file is in . and the dockerfile is Dockerfile.core
    build:
      context: .
      dockerfile: Dockerfile.core
    # Exposing port 3030 
    ports:
      - "3030:3030"
    depends_on:
      - cloudbanking-account
      - cloudbanking-card
      - cloudbanking-funds

    # Creating a network. It will be useful for connecting
    # multiple microservices here
    networks:
      - proxynet
    extra_hosts:
      # To comunicate host with docker container
      - "host.docker.internal:host-gateway"

  cloudbanking-account:
    container_name: cloudbanking-account
    build: 
      context: .
      dockerfile: Dockerfile.account
    ports:
      - "3031:3031"
    networks:
      - proxynet
    extra_hosts:
      - "host.docker.internal:host-gateway"

  cloudbanking-card:
    container_name: cloudbanking-card
    build: 
      context: ./card
      dockerfile: Dockerfile.cards
    ports:
      - "3032:3032"
    networks:
      - proxynet
    extra_hosts:
      - "host.docker.internal:host-gateway"

  cloudbanking-funds:
    container_name: cloudbanking-funds
    image: pepitoenpeligro/cloudbanking-funds
    ports:
      - "3033:3033"
    networks:
      - proxynet
    extra_hosts:
      - "host.docker.internal:host-gateway"



networks:
  proxynet:
    name: custom_network

Code Advance

HU 3 - Add investment fund as customer user HU 6 - Delete fund investment as customer user

Defining model:

package com.pepe.rest.resteasyjackson

class Fund{
    var id: String? = null
    var amount: Float? =  0.0f
    var dateStart: String? = null
    var dateEnd:   String? = null
    var status: Boolean? = false

    constructor() {}

    constructor(id: String?, amount: Float?, dateStart: String?, dateEnd: String?, status: Boolean?){
        this.id = id
        this.amount = amount
        this.dateStart = dateStart
        this.dateEnd = dateEnd
        this.status = status
    }
}

The object mapper (for json exchange)

@Singleton
class MyObjectMapperCustomizer : ObjectMapperCustomizer {
    override fun customize(objectMapper: ObjectMapper) {
        // To suppress serializing properties with null values
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL)
    }
}

The definition of the microservice that includes the fulfillment of both user stories:

package com.pepe.rest.resteasyjackson
import javax.inject.Inject
import javax.enterprise.inject.Default
import javax.enterprise.context.ApplicationScoped
import javax.ws.rs.core.Response.*
import javax.ws.rs.core.Response
import org.jboss.logging.Logger


import java.util.*
import javax.ws.rs.*
import javax.ws.rs.core.MediaType
import java.time.LocalDateTime


// ./mvnw compile quarkus:dev
@ApplicationScoped
class FundService {

    private val funds = Collections.newSetFromMap(Collections.synchronizedMap(LinkedHashMap<Fund, Boolean>()))


    fun greeting(name: String): String {
        return "hello $name"
    }

    fun addFund(f: Fund): Boolean{
        return funds.add(f)

    }


    fun getFunds(): Set<Fund>{
        return funds
    }

    fun deleteFund(id: String): Boolean{
        funds.removeIf {  existingFund:    Fund -> existingFund.id!!.contentEquals(id!!) }
        return true
    }


}

@Path("/")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
class FundResource {

    companion object {
        private val LOG = Logger.getLogger(FundResource::class.java)

    }

    @Inject
    @field: Default 
    lateinit var service: FundService

    // Get all funds
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("/funds")
    fun list():  Set<Fund> {
        LOG.info("GET -> returning funds")
        return service.getFunds()
    }

    // Creates one funds
    @POST
    @Path("/funds")
    fun  add(f: Fund): Response {
        val result = service.addFund(f)
        if(result){
            LOG.info("POST -> Fund was created")
            var m  = Message("Created", "Fund was created");
            return Response.status(Response.Status.ACCEPTED).entity(m).build();
        }
        LOG.info("POST -> Fund was NOT created")
        var m  = Message("Not Created", "Fund was not created");
        return Response.status(Response.Status.BAD_REQUEST).entity(m).build();

    }

    // Deletes one funds
    @DELETE
    @Path("/funds/{id}")
    fun delete(@PathParam("id") id: String): Boolean{
        LOG.info("DELETE -> Fund was deleted")
        return service.deleteFund(id)
    }

}

Benchmarking

Funds Microservice (kotlin-quarkus)

WRK

bench_wrk_funds

Taurus (JMeter)

bench_bzt_funds

You can find the original video (.mov) in https://user-images.githubusercontent.com/14912971/104126932-99aa3780-535f-11eb-9016-be4826f90b10.mov

Users Microservice (rust-actix)

WRK

bench_bzt_users2

Taurus (JMeter)

bench_wrk_funds

You can find the original video (.mov) in https://user-images.githubusercontent.com/14912971/104126945-a7f85380-535f-11eb-960e-afe8f77fc08a.mov

Does it work together?

docker-compose up

Using all routes

Demo

You can find the original video in https://user-images.githubusercontent.com/14912971/104130787-0975ed80-5373-11eb-8eaa-35ee4e42d511.mov

Development attempt with Elixir (absolute failure)