Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
50 changes: 50 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,53 @@ You can use Graphql;
When you finish your challenge, after forking a repository, you **must** open a pull request to our repository. There are no limitations to the implementation, you can follow the programming paradigm, modularization, and style that you feel is the most appropriate solution.

If you have any questions, please let us know.

# Solucion
Para manejar el requerimiento se ha creado dos microservicios (transaction-service, status-service).
Ambos se comunicar de manera asincrona a travez de dos Topicos Kafka (transaction-creation-topic, anti-fraud-validation-topic).

El primer microservicio expone dos endpoint, uno para la creacion de la transacion y segundo para listar las transacciones creadas.

El segundo microservicio para evaluar la transaccion apartir del monto (Si el monto es mayor de 1000 el monto es rejectado y si es menor es aceptado).

## Stack Tecnico
- Java 17
- Maven 3.2.0
- Apache Kafka 5.5.3
- Postgres 14
- Spring WebFlux
- R2DBC Connection
- Lombok

## Instalacion
1. Clonar el repositorio y entrar en la carpeta del proyecto
2. Para iniciar los servicios de postgres, zookeper, kafka corremos el siguiente docker comand
```sh
docker-compose up -d
```
3. Crear el nombre de la base de datos 'transactions_db'
4. Iniciar el microservicio transaction-service (Se iniciara en el puerto 8080)
5. Iniciar el microservicio status-service (se iniciara en el puerto 8081)

## Pruebas
1. Crear una transaccion (La transacion se creara con el estado PENDING, y se enviara al segundo microservicio a travez del topico de kafka para su evaluacion).
```curl
curl --location 'http://localhost:8080/api/v1/transactions' \
--header 'Content-Type: application/json' \
--data '{
"accountExternalIdDebit":"DEBIT_123_1",
"accountExternalIdCredit":"CREDIT_123_1",
"transferTypeId":1,
"value": 100
}'
```

2. El segundo Microservicio recibe el mensaje y evalua el monto es mayor a 1000 para devolver el estado REJECTED y si el monto es menor a 1000 devolvera APPROVED.

3. Para verificar si se cambio los estados segun la evaluacion, podemos usar el siguiete endpoint (Lista las transacciones creadas)
```curl
curl --location 'http://localhost:8080/api/v1/transactions
```



49 changes: 47 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,64 @@ services:
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
networks:
- microservices-network

zookeeper:
image: confluentinc/cp-zookeeper:5.5.3
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
networks:
- microservices-network
ports:
- "2181:2181"

kafka:
image: confluentinc/cp-enterprise-kafka:5.5.3
depends_on: [zookeeper]
depends_on:
- zookeeper
environment:
KAFKA_ZOOKEEPER_CONNECT: "zookeeper:2181"
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
KAFKA_BROKER_ID: 1
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_JMX_PORT: 9991
KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true'
KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE: 'true'
KAFKA_NUM_PARTITIONS: 3
KAFKA_DEFAULT_REPLICATION_FACTOR: 1
ports:
- 9092:9092
- "9092:9092"
- "29092:29092"
networks:
- microservices-network

kafka-init:
image: confluentinc/cp-kafka:7.5.0
container_name: kafka-init
depends_on:
- kafka
entrypoint: ['/bin/sh', '-c']
command: |
"
echo 'Waiting for Kafka to be ready...'
sleep 10

echo 'Creating topics...'
kafka-topics --bootstrap-server kafka:9092 --create --if-not-exists --topic transaction-creation-topic --partitions 3 --replication-factor 1
kafka-topics --bootstrap-server kafka:9092 --create --if-not-exists --topic anti-fraud-validation-topic --partitions 3 --replication-factor 1

echo 'Listing topics...'
kafka-topics --bootstrap-server kafka:9092 --list

echo 'Topics created successfully!'
"
networks:
- microservices-network

networks:
microservices-network:
driver: bridge
5 changes: 5 additions & 0 deletions status-service/.idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions status-service/.idea/compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions status-service/.idea/encodings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions status-service/.idea/jarRepositories.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions status-service/.idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions status-service/.idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions status-service/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
FROM maven:3.9-eclipse-temurin-17-alpine AS build

WORKDIR /app

# Copiar archivos de Maven
COPY pom.xml .
RUN mvn dependency:go-offline -B

# Copiar código fuente y compilar
COPY src ./src
RUN mvn clean package -DskipTests

# Imagen final
FROM eclipse-temurin:17-jre-alpine

WORKDIR /app

# Copiar el JAR compilado
COPY --from=build /app/target/*.jar app.jar

# Exponer puerto
EXPOSE 8081

# Punto de entrada
ENTRYPOINT ["java", "-jar", "app.jar"]
98 changes: 98 additions & 0 deletions status-service/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/>
</parent>

<groupId>com.example</groupId>
<artifactId>status-service</artifactId>
<version>1.0.0</version>
<name>status-service</name>
<description>Microservicio de actualización de estados con WebFlux y Kafka</description>

<properties>
<java.version>17</java.version>
</properties>

<dependencies>
<!-- Spring WebFlux -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

<!-- Reactor Kafka -->
<dependency>
<groupId>io.projectreactor.kafka</groupId>
<artifactId>reactor-kafka</artifactId>
</dependency>

<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

<!-- Validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

<!-- Actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.example.statusservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class StatusServiceApplication {

public static void main(String[] args) {
SpringApplication.run(StatusServiceApplication.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.example.statusservice.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JacksonConfig {

@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
return mapper;
}
}
Loading