In real estate markets, it can make a difference to have real-time data instead of the good-old batch processing.
How many real estate agents are losing time because they cannot find their listing?
How many home-seekers lost opportunities, hearing things like “sorry, it’s already sold”?
In this talk I will be presenting how we implemented an event-driven architecture based on Apache Kafka, how it is enabling us to deliver fresh content to all LifullConnect sites across 3 continents, and build meaningful products to real estate professionals, and home-seekers.
48. How we implemented it?
ListingHasBeenPublished
event_id: UUID //a unique ID
produced_by: String //owner of data
occurred_on: Date //when it happened
id: String //which ID in the domain refers
listing: …
…
49. How we implemented it?
ListingHasBeenPublished
event_id: UUID //a unique ID
produced_by: String //owner of data
occurred_on: Date //when it happened
id: String //which ID in the domain refers
listing: …
…
ListingHasBeenModified
event_id: UUID //a unique ID
produced_by: String //owner of data
occurred_on: Date //when it happened
id: String //which ID in the domain refers
modifications: …
…
50. How we implemented it?
ListingHasBeenPublished
event_id: UUID //a unique ID
produced_by: String //owner of data
occurred_on: Date //when it happened
id: String //which ID in the domain refers
listing: …
…
ListingHasBeenModified
event_id: UUID //a unique ID
produced_by: String //owner of data
occurred_on: Date //when it happened
id: String //which ID in the domain refers
modifications: …
…
ListingHasBeenBoosted
event_id: UUID //a unique ID
produced_by: String //owner of data
occurred_on: Date //when it happened
id: String //which ID in the domain refers
boosting: …
…
51. Apache Kafka
● Implements a log
● Distributed in a cluster of broker nodes
● Fault tolerant through partitions & replicas
● Enables multiple consumers of data
● Enables real-time consumption of data
63. Apache Kafka + Apache Avro
● Send Avro binary records to Kafka
● Every single record has a schema, registered into a
schema registry
● Schema registry allows controlled schema evolution
65. Apache Kafka + Apache Avro
Producer
Schema Registry
Datum A
Datum A
Datum B
Datum B
Kafka Cluster
66. Apache Kafka + Apache Avro
Producer
Schema Registry
Datum A
Datum A
Datum B
Datum B
Kafka Cluster
1 �����
Schema A
1
67. Apache Kafka + Apache Avro
Producer
Schema Registry
Datum A
Datum A
Datum B
Datum B
Kafka Cluster
1 �����
Schema A
1
1 �����
68. Apache Kafka + Apache Avro
Producer
Schema Registry
Datum A
Datum A
Datum B
Datum B
Kafka Cluster
1 �����
Schema A
1
1 �����
Schema B
2
2
2
�����
�����
69. Apache Kafka + Apache Avro
1
1
2
2
�����
�����
�����
�����
Consumer
Producer
Schema A
1
Schema B
2
Schema Registry
Datum A
Datum A
Datum B
Datum B
Kafka Cluster
70. Apache Kafka + Apache Avro
1
1
2
2
�����
�����
�����
�����
Consumer
Producer
Schema A
1
Schema B
2
Schema Registry
Datum A
Datum A
Datum B
Datum B
Kafka Cluster
71. Apache Kafka + Apache Avro
1
1
2
2
�����
�����
�����
�����
Consumer
Producer
Schema A
1
Schema B
2
Schema Registry
Datum A
Datum A
Datum B
Datum B
Kafka Cluster
Datum A
72. Apache Kafka + Apache Avro
1
1
2
2
�����
�����
�����
�����
Consumer
Producer
Schema A
1
Schema B
2
Schema Registry
Datum A
Datum A
Datum B
Datum B
Kafka Cluster
Datum A
Datum A
73. Apache Kafka + Apache Avro
1
1
2
2
�����
�����
�����
�����
Consumer
Producer
Schema A
1
Schema B
2
Schema Registry
Datum A
Datum A
Datum B
Datum B
Kafka Cluster
Datum A
Datum A
Datum B
Datum B
74. Apache Kafka + Apache Avro
1
1
2
2
�����
�����
�����
�����
Consumer
Producer
Schema A
1
Schema B
2
Schema Registry
Datum A
Datum A
Datum B
Datum B
Datum A’
Kafka Cluster
Datum A
Datum A
Datum B
Datum B
75. Apache Kafka + Apache Avro
1
1
2
2
�����
�����
�����
�����
Consumer
Producer
Schema A
1
Schema B
2
Schema Registry
Datum A
Datum A
Datum B
Datum B
Datum A’
3 �����
Schema A’
3
Kafka Cluster
Datum A
Datum A
Datum B
Datum B
Since it’s an evolution, it
will check compatibility.
Fail if broken.
76. Apache Kafka + Apache Avro
1
1
2
2
�����
�����
�����
�����
Consumer
Producer
Schema A
1
Schema B
2
Schema Registry
Datum A
Datum A
Datum B
Datum B
Datum A’
3 �����
Schema A’
3
Kafka Cluster
Datum A
Datum A
Datum B
Datum B
Datum A’
No compatibility broken,
so can read without
problem.
77. How we propagate data?
ListingHasBeenPublished
ListingHasBeenModified
ListingHasBeenHidden
ListingHasBeenModified
ListingHasBeenModified
ListingHasBeenPublished
78. What is LIFULL Connect?
The real-time listings system
How we propagate data?
How we process listings?
How we serve listings?
82. ● Framework to do transformations to data stored in
Kafka
● Has an easy-to-use functional API, that runs on JVM
● Does the data processing in streaming
● Ensures exactly once delivery
● Distributed
● Fault tolerant
● Enables stateless and stateful processing of
information
Apache Kafka Streams
83. Apache Kafka Streams
import org.apache.kafka.streams.
KafkaStreams
import org.apache.kafka.streams.
StreamsBuilder
import org.apache.kafka.streams.kstream.
KStream
import java.util.
Properties
fun main() {
val streamsBuilder = StreamsBuilder()
val eventsStream
: KStream<Key, Value> = streamsBuilder
.stream("events")
val transformedStream
: KStream<Key2, Value2> = eventsStream
.map { key, value ->
TODO("do a transformation"
)
}
transformedStream
.to("eventsTransformed"
)
val topology = streamsBuilder
.build()
val kafkaStreams = KafkaStreams(
topology, Properties())
kafkaStreams
.start()
}
Stateless Kafka Streams Application
events
eventsTransformed
84. Apache Kafka Streams
import org.apache.kafka.streams.
KafkaStreams
import org.apache.kafka.streams.
StreamsBuilder
import org.apache.kafka.streams.kstream.
KStream
import java.util.
Properties
fun main() {
val streamsBuilder = StreamsBuilder()
val eventsStream: KStream<Key, Value> = streamsBuilder.stream("events")
val transformedStream: KStream<Key2, Value2> = eventsStream.map
{ key, value ->
TODO("do a transformation")
}
transformedStream.to("eventsTransformed")
val topology = streamsBuilder
.build()
val kafkaStreams = KafkaStreams(
topology, Properties())
kafkaStreams
.start()
}
Stateless Kafka Streams Application
events
eventsTransformed
Boilerplate
85. Apache Kafka Streams
import org.apache.kafka.streams.KafkaStreams
import org.apache.kafka.streams.StreamsBuilder
import org.apache.kafka.streams.kstream.KStream
import java.util.Properties
fun main() {
val streamsBuilder = StreamsBuilder()
val eventsStream
: KStream<Key, Value> = streamsBuilder
.stream("events")
val transformedStream: KStream<Key2, Value2> = eventsStream.map
{ key, value ->
TODO("do a transformation")
}
transformedStream.to("eventsTransformed")
val topology = streamsBuilder.build()
val kafkaStreams = KafkaStreams(topology, Properties())
kafkaStreams.start()
}
Stateless Kafka Streams Application
events
eventsTransformed
Read from a topic
86. Apache Kafka Streams
import org.apache.kafka.streams.KafkaStreams
import org.apache.kafka.streams.StreamsBuilder
import org.apache.kafka.streams.kstream.KStream
import java.util.Properties
fun main() {
val streamsBuilder = StreamsBuilder()
val eventsStream: KStream<Key, Value> = streamsBuilder.stream("events")
val transformedStream
: KStream<Key2, Value2> = eventsStream
.map { key, value ->
TODO("do a transformation"
)
}
transformedStream.to("eventsTransformed")
val topology = streamsBuilder.build()
val kafkaStreams = KafkaStreams(topology, Properties())
kafkaStreams.start()
}
Stateless Kafka Streams Application
events
eventsTransformed
Do a transformation
87. Apache Kafka Streams
import org.apache.kafka.streams.KafkaStreams
import org.apache.kafka.streams.StreamsBuilder
import org.apache.kafka.streams.kstream.KStream
import java.util.Properties
fun main() {
val streamsBuilder = StreamsBuilder()
val eventsStream: KStream<Key, Value> = streamsBuilder.stream("events")
val transformedStream: KStream<Key2, Value2> = eventsStream.map
{ key, value ->
TODO("do a transformation")
}
transformedStream
.to("eventsTransformed"
)
val topology = streamsBuilder.build()
val kafkaStreams = KafkaStreams(topology, Properties())
kafkaStreams.start()
}
Stateless Kafka Streams Application
events
eventsTransformed
Write to a topic
88. Apache Kafka Streams
import org.apache.kafka.streams.
KafkaStreams
import org.apache.kafka.streams.
StreamsBuilder
import org.apache.kafka.streams.kstream.
KStream
import java.util.
Properties
fun main() {
val streamsBuilder = StreamsBuilder()
val eventsStream
: KStream<Key, Value> = streamsBuilder
.stream("events")
val transformedStream
: KStream<Key2, Value2> = eventsStream
.map { key, value ->
TODO("do a transformation"
)
}
transformedStream
.to("eventsTransformed"
)
val topology = streamsBuilder
.build()
val kafkaStreams = KafkaStreams(
topology, Properties())
kafkaStreams
.start()
}
Stateless Kafka Streams Application
events
eventsTransformed
105. Kafka Connect
● Software to connect Kafka data to external systems
● Move data in Kafka with source connectors
● Move data out of Kafka with sink connectors
● Pluggable with third party or custom connectors
120. To-do list
● Better observability
● Conventions with Public vs Private topics
● Proper data catalog
● Other divisions using Kafka, avoiding the caveats of a
multi-tenant cluster
● …
121. Potential unlocked
We do more than just serve
listings
Like, adapt the listing depending on its popularity, in real-time!