DonanımHaber

Hexagonal Architecture etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
Hexagonal Architecture etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

Hexagonal Mimari



Bu görsel, bir yazılım uygulamasının mimarisini temsil eden bir diyagramdır. Temel olarak, temiz mimari veya hegzagonal mimari olarak bilinen bir yapıyı göstermektedir. Temiz mimari, bir uygulamanın farklı katmanlarını nasıl düzenleyeceğinizi ve bu katmanların birbirleriyle nasıl etkileşimde bulunacağını açıklar. İşte bu mimarinin ana bileşenleri ve bunların işlevleri:


Çekirdek (Core): Uygulamanın kalbidir ve Domain business logic'i içerir. Bu, uygulamanın temel iş kurallarını ve uygulama hizmetlerini tanımlar. Çekirdek, ayrıca Entity'leri ve Aggregate'leri içerir; bunlar iş kurallarını ve uygulama veri modellerini temsil eder. Çekirdek katmanın içindeki her şey uygulamanın temel davranışını tanımlar ve bu katmanın dışındaki herhangi bir şeyle doğrudan etkileşime girmemelidir.


Entity/Aggregate: Bu, uygulamanın iş kurallarını içeren ve genellikle bir veritabanında saklanan verileri temsil eder. Entity'ler benzersiz bir kimliğe sahip olurken, Aggregate'ler ise birden çok Entity veya Value Object'i kapsayabilir.


Uygulama Hizmetleri (Application Services): Bu katman, uygulama akışını yönetir ve Entity'lerle Domain Logic üzerinde işlemler gerçekleştirir.


Portlar ve Adapterlar: Bu yapı, uygulamanın dış dünyayla etkileşim kurmasını sağlar. Portlar, uygulamanın dışarıya açılan noktalarıdır ve genellikle API çağrıları veya veritabanı erişimi gibi girdi/çıktı işlemleri için kullanılır. Adapterlar, dış kaynaklarla (örneğin, veritabanları, web servisleri, e-posta servisleri gibi) etkileşime girer ve bu dış kaynakların uygulamanın iç yapısına entegre edilmesini sağlar.


Altyapı (Infrastructure): Bu katman, uygulamanın teknoloji ve çerçevelerini içerir. Altyapı adapterları, ORM (Object-Relational Mapping) şemaları, veritabanı adapterları ve dış kaynak adapterları gibi farklı teknolojilere ait bileşenlerden oluşur.


Arayüz Adapteerleri (Interface Adapters): Bu, kullanıcı arayüzleri, veri transfer objeleri (DTO'lar), kontrol cihazları ve sunumcuları içeren katmandır. Kullanıcıların ve diğer sistemlerin uygulamayla etkileşimde bulunmasını sağlar.


HTTP API'si ve DTO Kontrolleri: Bu, dış sistemlerin veya kullanıcıların uygulama ile HTTP protokolü üzerinden iletişime geçtiği arayüzlerdir.


Sunucular (Presenters) ve CLI Kontrolleri: Kullanıcıya görsel çıktı sunan veya komut satırı arayüzü üzerinden etkileşimi sağlayan bileşenlerdir.


Veritabanı (DB): Uygulamanın verilerini saklayan ve genellikle SQL (PostgreSQL, MySQL) veya NoSQL (MongoDB gibi) teknolojilerini kullanarak erişilen depodur.


Diyagram ayrıca, çeşitli kullanıcı tiplerini (son kullanıcılar, teknik kullanıcılar ve tüketici sistemler) ve uygulamanın farklı bileşenleri arasındaki etkileşim akışlarını göstermektedir. Bu etkileşimler arasında komutlar, olaylar, sorgular ve mesajlar bulunmaktadır. Diyagram, uygulama mimarisinin modüler ve esnek olmasını sağlamak için tasarlanmıştır, böylece farklı katmanlar arasındaki bağımlılıklar azaltılır ve uygulamanın genişletilebilirliği artırılır.


Diyagramdaki detaylara daha derinlemesine bakarsak:


Domain Services: Bu, bir veya birden fazla Entity arasında ya da Entity ve Value Object'ler arasındaki domain mantığını yürütür. Bunlar, çekirdek iş mantığını gerçekleştiren ve genellikle çekirdek iş kurallarının bulunduğu servislerdir.


Value Objects: Bunlar, genellikle bir Entity veya Aggregate ile ilişkili değerleri temsil eder ve bu değerlere iş mantığı uygulayan nesnelerdir. Value Objects'lerin kendilerine ait bir kimliği yoktur ve genellikle değiştirilemezlerdir.


Event Controllers ve CLI Controllers: Bunlar, kullanıcı eylemlerini ve sistem olaylarını yakalayan ve bunlara yanıt veren bileşenlerdir. Örneğin, bir kullanıcı bir form gönderdiğinde veya bir zamanlayıcı bir olay tetiklediğinde bu kontrolörler devreye girer.


External Events: Bu, uygulamanın dış dünyadan gelen olaylara tepki vermesi gerektiğinde kullanılır. Örneğin, bir kullanıcının bir aksiyonu veya bir mikroservisten gelen bir mesaj gibi.


Database Adapters: Bunlar, uygulamanın veritabanı ile etkileşimini yönetir. ORM şemaları ve veritabanı sorguları bu katmanda yer alır.


External Resource Adapters: Uygulamanın e-posta servisleri, SMS servisleri ve üçüncü taraf API'leri gibi dış kaynaklarla etkileşimini sağlar.


Microservices Adapters ve Message Queue: Bu, uygulamanın mikroservis mimarisine entegre olmasını ve asenkron mesajlaşmayı destekler.


Diyagramın genel yapısı, farklı katmanların birbirinden bağımsız çalışmasını ve bu sayede geliştirme, bakım ve ölçeklendirme işlemlerinin kolaylaştırılmasını amaçlar. İlişkisel veritabanlarından mikroservislere, CLI araçlarından web arayüzlerine kadar tüm bileşenlerin birbiriyle nasıl etkileşime gireceğini anlamak için diyagram detaylı bir rehber sağlar.


Diyagramın sağladığı bu yapının, yazılım geliştiricilerine, sistem mimarlarına ve teknik proje yöneticilerine hitap ettiğini ve karmaşık uygulama mimarilerini planlamalarında onlara yardımcı olacak bir çerçeve sunduğunu da belirtmek önemlidir. Bu mimari, özellikle büyük ve karmaşık sistemlerde, sistemin daha yönetilebilir, esnek ve ölçeklenebilir olmasını sağlamak için kullanılır.


Diyagramın etrafındaki renkli alanlar ve onlar arasındaki akışlar, çeşitli katmanlar ve bileşenler arasında veri ve komut akışını temsil eder. İşte bu akışların bazı detayları:


Kırmızı Oklar (Commands): Kullanıcıların veya sistemlerin eylemlerini temsil eder. Bu komutlar, uygulama tarafından alınır ve çekirdek katman içinde işlenir. Örneğin, bir kullanıcı bir formu gönderdiğinde bu bir komut oluşturabilir.


Yeşil Oklar (Queries): Sistemden veri talep etmek için kullanılır. Bu sorgular, çekirdek katmandaki verileri okur ve kullanıcı arayüzüne sunar.


Mavi Oklar (DTO - Data Transfer Objects): Katmanlar arası veri transferini temsil eder. Bu nesneler, katmanlar arası iletişimde kullanılan veri yapılarıdır ve genellikle veritabanı kayıtlarından alınan verileri kullanıcı arayüzüne uygun biçimde dönüştürmek için kullanılır.


Port ve Adapter Mekanizması: Bu mekanizma, çekirdek katmanın dış dünyadan soyutlanmasını sağlar. Portlar, dış dünya ile çekirdek katman arasında bir arayüz görevi görür ve adapterlar, bu portlardan gelen veya bu portlara giden veri akışını yönetir.


Altın Sarısı Çizgiler (Infrastructure Adapters): Bu çizgiler, altyapı katmanının çeşitli bileşenleri arasındaki etkileşimleri gösterir. Örneğin, bir veritabanı adapterının bir ORM şemasıyla nasıl etkileşime girdiğini temsil eder.


Turuncu Alan (Application Services): Uygulamanın iş mantığını orkestra eden ve dış dünya ile etkileşimi sağlayan servisler burada konumlandırılmıştır.


Mavi Alan (Entities/Aggregates): Çekirdeğin merkezinde yer alan bu alan, iş mantığı ve veri modellerini içeren nesneleri temsil eder.


Yeşil Alan (Interface Adapters): Kullanıcı arayüzleri, komut satırı arayüzleri ve diğer giriş/çıkış bileşenlerinin bulunduğu katmandır.


Diyagramın genel mantığı, uygulamanın esnek ve modüler bir yapıda olmasını sağlamak, böylece farklı katmanlar ve bileşenler arasındaki bağımlılıkları en aza indirgemektir. Bu yaklaşım, yazılımın bakımını ve güncellemelerini kolaylaştırır, ayrıca yeni özelliklerin veya bileşenlerin entegrasyonunu basitleştirir.


Sonuç olarak, bu diyagramın temsil ettiği mimari, yazılım geliştirme süreçlerinde sağlam ve sürdürülebilir uygulamalar oluşturmanın yanı sıra, iş ve veri akışını düzenleyerek karmaşık sistemlerin yönetilmesine olanak tanıyan bir yapı sunar. Bu yaklaşım, özellikle büyük ölçekli ve uzun vadeli projelerde tercih edilir, çünkü sistemin farklı bölümlerinin bağımsız olarak geliştirilmesine, test edilmesine ve dağıtılmasına imkan tanır.


Örnek uygulama koduna buradan ulaşabilirisiniz.

Spring, Spring Data, Java 11, H2 DB ve Hibernate ile Rich domain model kullanarak aggregateler value objectler entityler kullanarak advanced bir senaryo ile örnek bir senaryo ile DDD projesini açıklayarak kodla. Port ve Adaptör ile Kafka ve Hazelcast Cache Entegrasyonunu sağla. Event Sourcing ekle.




Product Entity - Stok Kontrolü Dahil


@Entity

public class Product {


    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    private Long id;

    private String name;

    private double price;

    private int stockQuantity;


    public void decreaseStock(int quantity) {

        if (stockQuantity < quantity) {

            throw new IllegalStateException("Not enough stock");

        }

        stockQuantity -= quantity;

    }


    public void increaseStock(int quantity) {

        stockQuantity += quantity;

    }


    // Diğer alanlar ve metodlar

}

Order Entity - Sipariş Durumu Değişiklikleri Dahil

@Entity

public class Order {


    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    private Long id;


    @Enumerated(EnumType.STRING)

    private OrderStatus status;


    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)

    private List<OrderLine> orderLines;


    public void place() {

        if (status != OrderStatus.NEW) {

            throw new IllegalStateException("Can only place a new order");

        }

        status = OrderStatus.PLACED;

    }


    public void ship() {

        if (status != OrderStatus.PLACED) {

            throw new IllegalStateException("Can only ship a placed order");

        }

        status = OrderStatus.SHIPPED;

        for (OrderLine line : orderLines) {

            line.getProduct().decreaseStock(line.getQuantity());

        }

    }


    public void cancel() {

        if (status != OrderStatus.PLACED) {

            throw new IllegalStateException("Can only cancel a placed order");

        }

        status = OrderStatus.CANCELLED;

        for (OrderLine line : orderLines) {

            line.getProduct().increaseStock(line.getQuantity());

        }

    }


    // Diğer alanlar ve metodlar

}

Bu, daha zengin bir domain modeli oluşturur. İş mantığı, servis katmanından alınıp model nesnelerine yerleştirilmiştir. Bu yaklaşım, daha temiz bir kod ve model içerisinde daha çok iş mantığını ifade eder.


Tamam, bu zengin domain modeli etrafında servis ve repository katmanlarını oluşturalım.


Repository Katmanı

Önceki açıklamalarda zaten tanımlanan repository sınıflarını kullanabiliriz (ProductRepository, OrderRepository vb.). Fakat bu modelde iş mantığı daha çok domain katmanında olduğu için, repository'nin rolü veritabanı işlemlerini gerçekleştirmekle sınırlıdır.


Servis Katmanı

Zengin domain modeliyle birlikte servis katmanının karmaşıklığı azalır. İş mantığı artık modelde yer aldığı için servis katmanı daha çok depolama ve altyapı işlevleriyle ilgilenir.


ProductService


@Service

public class ProductService {


    @Autowired

    private ProductRepository productRepository;


    public Product findById(Long id) {

        return productRepository.findById(id).orElse(null);

    }


    public Product save(Product product) {

        return productRepository.save(product);

    }

}

OrderService


@Service

public class OrderService {


    @Autowired

    private OrderRepository orderRepository;


    @Autowired

    private ProductService productService;


    public List<Order> findAll() {

        return orderRepository.findAll();

    }


    public Order findById(Long id) {

        return orderRepository.findById(id).orElse(null);

    }


    public Order placeOrder(Order order) {

        for (OrderLine line : order.getOrderLines()) {

            Product product = productService.findById(line.getProduct().getId());

            if (product == null) {

                throw new IllegalArgumentException("Product not found");

            }

            product.decreaseStock(line.getQuantity());

            productService.save(product);

        }

        order.place();

        return orderRepository.save(order);

    }


    public void shipOrder(Long orderId) {

        Order order = findById(orderId);

        if (order == null) {

            throw new IllegalArgumentException("Order not found");

        }

        order.ship();

        orderRepository.save(order);

    }


    public void cancelOrder(Long orderId) {

        Order order = findById(orderId);

        if (order == null) {

            throw new IllegalArgumentException("Order not found");

        }

        for (OrderLine line : order.getOrderLines()) {

            Product product = productService.findById(line.getProduct().getId());

            product.increaseStock(line.getQuantity());

            productService.save(product);

        }

        order.cancel();

        orderRepository.save(order);

    }

}

Bu yapıda, OrderService ve ProductService sınıfları daha sade bir yapıya sahip. Modelde bulunan iş mantığı sayesinde servisler daha az sorumluluğa sahip.


Controller Katmanı

Controller katmanı da önceki şekilde kalabilir. Fakat artık bazı endpoint'lerde doğrudan servislerin yerine modelde tanımlanan iş mantığına dayalı olarak işlemleri gerçekleştirebilirsiniz.


Sonuç olarak, bu yaklaşımla, iş mantığı daha çok domain katmanında bulunurken, servis ve controller katmanları daha az karmaşık ve daha az işlevsellik taşır. Bu sayede kodun okunabilirliği, bakımı ve genişletilmesi daha kolay olur.


Bu aşamadan sonra eklememi veya açıklamamı istediğiniz başka bir konu var mı?


Bu uygulamaya adaptörler ve portlar kullanarak apache kafka entegre edebilir misin?


Elbette, Domain-Driven Design (DDD) yaklaşımında kullanılan hexagonal architecture (ya da ports and adapters architecture) sayesinde, uygulamanın çekirdeğini (domain) dış dünyadan (örneğin, Kafka gibi mesajlaşma sistemlerinden) izole edebiliriz.


Apache Kafka, veri yayını ve tüketimi için kullanılan açık kaynaklı bir stream-processing yazılım platformudur. Bu örnek için, sipariş durumlarının değişikliklerini Kafka'ya göndermek ve bu değişiklikler üzerinde dinleyiciler oluşturmak istediğimizi varsayalım.


1. Ports Tanımlama

Portlar, uygulamanın iç ve dış dünyası arasında bir sınır oluşturur. İlk olarak, bir port tanımlayarak bu sınırı ifade edelim:


public interface OrderStatusChangedPort {

    void orderStatusChanged(Long orderId, OrderStatus status);

}

Bu port, sipariş durumu değiştiğinde çağrılacak olan metodu tanımlar.


2. Kafka Adapter Oluşturma

Bu port için Kafka ile entegrasyon sağlayan bir adapter yazmalıyız. Bu adapter, Kafka'ya mesaj gönderecek olan bileşendir.


@Service

public class KafkaOrderStatusChangedAdapter implements OrderStatusChangedPort {


    private final KafkaTemplate<String, String> kafkaTemplate;


    @Value("${kafka.topic.order-status}")

    private String orderStatusTopic;


    @Autowired

    public KafkaOrderStatusChangedAdapter(KafkaTemplate<String, String> kafkaTemplate) {

        this.kafkaTemplate = kafkaTemplate;

    }


    @Override

    public void orderStatusChanged(Long orderId, OrderStatus status) {

        String message = "Order ID: " + orderId + ", Status: " + status.name();

        kafkaTemplate.send(orderStatusTopic, message);

    }

}

3. Domain Katmanında Port Kullanma

OrderService içerisinde, sipariş durumu değiştikçe port'u kullanarak bu durumu dış dünyaya bildireceğiz:


@Service

public class OrderService {


    @Autowired

    private OrderStatusChangedPort orderStatusChangedPort;


    // Diğer metotlar


    public void shipOrder(Long orderId) {

        Order order = findById(orderId);

        if (order == null) {

            throw new IllegalArgumentException("Order not found");

        }

        order.ship();

        orderRepository.save(order);

        orderStatusChangedPort.orderStatusChanged(orderId, OrderStatus.SHIPPED);

    }


    public void cancelOrder(Long orderId) {

        Order order = findById(orderId);

        if (order == null) {

            throw new IllegalArgumentException("Order not found");

        }

        order.cancel();

        orderRepository.save(order);

        orderStatusChangedPort.orderStatusChanged(orderId, OrderStatus.CANCELLED);

    }

}

4. Kafka Konfigürasyonu

Son olarak, Kafka'yı Spring uygulamanızda kullanmak için gerekli konfigürasyonları eklemelisiniz. application.properties dosyasına şunları ekleyebilirsiniz:


spring.kafka.bootstrap-servers=localhost:9092

kafka.topic.order-status=order-status-topic

Ve bir KafkaConfig sınıfı oluşturarak gereken bean'leri tanımlayabilirsiniz:


@Configuration

public class KafkaConfig {


    @Value("${spring.kafka.bootstrap-servers}")

    private String bootstrapServers;


    @Bean

    public KafkaTemplate<String, String> kafkaTemplate() {

        return new KafkaTemplate<>(producerFactory());

    }


    @Bean

    public ProducerFactory<String, String> producerFactory() {

        Map<String, Object> configProps = new HashMap<>();

        configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);

        configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);

        configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);

        return new DefaultKafkaProducerFactory<>(configProps);

    }

}

Bu yapı, Apache Kafka ile entegre bir şekilde sipariş durumu değişikliklerini izlemenizi sağlar. Bu durum değişikliklerine dinleyiciler eklemek veya daha fazla entegrasyon oluşturmak da mümkündür.


Bu yaklaşımın güzel yanı, Kafka ile entegrasyonun tamamen izole edilmesi ve domain katmanının bu ayrıntılardan haberdar olmamasıdır. Bu, kodun daha temiz, bakımı daha kolay ve değişikliklere karşı daha esnek olmasına yardımcı olur.


Projeye port ve adaptör ile Hazelcast cache ekle


Tabii ki! Hazelcast, dağıtık bir veri ızgarası ve önbellekleme çözümüdür. Hexagonal mimaride, Hazelcast ile entegrasyonu port ve adaptörler aracılığıyla yapabiliriz. Bu sayede, iş mantığınızın önbellekleme mantığından tamamen izole edilmesi sağlanır.


1. Cache Portu Tanımlama

İlk olarak, önbellekleme işlemlerini tanımlayacak bir port oluşturmalıyız. Bu port, önbelleğe erişmek istediğimiz işlemleri ifade eder.


public interface CachePort {

    <T> T get(String key);


    void put(String key, Object value);


    void remove(String key);

}

2. Hazelcast Cache Adaptörü Oluşturma

Bu port için Hazelcast ile entegrasyon sağlayan bir adaptör yazmalıyız. Adaptör, portun metotlarını Hazelcast'a özgü çağrılara dönüştürür.


@Service

public class HazelcastCacheAdapter implements CachePort {


    private final HazelcastInstance hazelcastInstance;


    @Autowired

    public HazelcastCacheAdapter(HazelcastInstance hazelcastInstance) {

        this.hazelcastInstance = hazelcastInstance;

    }


    @Override

    public <T> T get(String key) {

        return (T) hazelcastInstance.getMap("default").get(key);

    }


    @Override

    public void put(String key, Object value) {

        hazelcastInstance.getMap("default").put(key, value);

    }


    @Override

    public void remove(String key) {

        hazelcastInstance.getMap("default").remove(key);

    }

}

3. Hazelcast Konfigürasyonu

HazelcastInstance bean'ini oluşturmak için bir konfigürasyon sınıfı eklemeliyiz:


@Configuration

public class HazelcastConfig {


    @Bean

    public Config hazelCastConfig() {

        Config config = new Config();

        config.setInstanceName("hazelcast-instance")

              .addMapConfig(new MapConfig()

                    .setName("default")

                    .setTimeToLiveSeconds(20));

        return config;

    }


    @Bean

    public HazelcastInstance hazelcastInstance(Config hazelCastConfig) {

        return Hazelcast.newHazelcastInstance(hazelCastConfig);

    }

}

4. Servis Katmanında Cache Kullanma

Şimdi, önbellek portunu servis katmanında kullanabiliriz. Örnek olarak, siparişlerin detaylarını önbelleklemek isteyebiliriz:

@Service

public class OrderService {


    @Autowired

    private CachePort cachePort;


    public OrderDetail getOrderDetail(Long orderId) {

        String key = "orderDetail:" + orderId;

        OrderDetail orderDetail = cachePort.get(key);

        if (orderDetail == null) {

            orderDetail = // Veritabanından sipariş detaylarını alın.

            cachePort.put(key, orderDetail);

        }

        return orderDetail;

    }

}

Bu yapı sayesinde, Hazelcast veya başka bir önbellekleme çözümüne geçiş yapmak istediğinizde, yalnızca yeni bir adaptör yazmanız gerekir. Bu, uygulamanın diğer kısımlarının değişiklik yapmadan aynı port üzerinden çalışmasına devam etmesini sağlar.


Event Sourcing de ekler misin?


Tabii ki, Event Sourcing'u bu mimariye eklemek ilginç ve karmaşık bir yön olabilir. Event Sourcing, her değişikliği olay (event) olarak saklamak yerine, uygulamanın durumunu bu olaylardan yeniden oluşturmanızı sağlar. Bu, daha güçlü bir denetim günlüğü ve karmaşık iş süreçlerinde daha fazla esneklik sağlayabilir.


Bu örnekte, sipariş durum değişikliklerinin olaylarını saklayacağımızı varsayalım.


1. Olay Tanımları

Her bir olay, belirli bir değişikliği temsil eder. Bu örnekte, sipariş durumlarının değiştiği olayları tanımlayalım:


public abstract class OrderEvent {

    private final Long orderId;

    private final LocalDateTime timestamp;


    protected OrderEvent(Long orderId) {

        this.orderId = orderId;

        this.timestamp = LocalDateTime.now();

    }


    public Long getOrderId() {

        return orderId;

    }


    public LocalDateTime getTimestamp() {

        return timestamp;

    }

}


public class OrderPlacedEvent extends OrderEvent {

    public OrderPlacedEvent(Long orderId) {

        super(orderId);

    }

}


public class OrderShippedEvent extends OrderEvent {

    public OrderShippedEvent(Long orderId) {

        super(orderId);

    }

}


public class OrderCancelledEvent extends OrderEvent {

    public OrderCancelledEvent(Long orderId) {

        super(orderId);

    }

}

2. Event Store

Olayları saklamak için bir olay deposu oluşturmalıyız. Bu depo, olayların saklanmasından ve sorgulanmasından sorumlu olacak.


@Repository

public interface EventStore extends JpaRepository<OrderEvent, Long> {

    List<OrderEvent> findByOrderIdOrderByTimestampAsc(Long orderId);

}

3. Event Publisher

Olayları yayınlayan bir sınıf oluşturacağız:


@Service

public class EventPublisher {


    @Autowired

    private EventStore eventStore;


    public void publish(OrderEvent event) {

        eventStore.save(event);

    }

}

4. Aggregate Root İle İntegrasyon

Order sınıfı, siparişle ilgili olayları yayınlamalı. Bu, Aggregate Root'un görevidir.


@Entity

public class Order {


    // Diğer alanlar


    @Transient

    private List<OrderEvent> newEvents = new ArrayList<>();


    public void place() {

        // Logic

        newEvents.add(new OrderPlacedEvent(this.id));

    }


    public void ship() {

        // Logic

        newEvents.add(new OrderShippedEvent(this.id));

    }


    public void cancel() {

        // Logic

        newEvents.add(new OrderCancelledEvent(this.id));

    }


    public List<OrderEvent> getNewEvents() {

        return Collections.unmodifiableList(newEvents);

    }

}

5. Event Sourcing ile Servis Katmanı İntegrasyonu

Servis katmanında, olayları toplayıp yayınlamalıyız:



@Service

public class OrderService {


    @Autowired

    private EventPublisher eventPublisher;


    // Diğer metotlar


    @Transactional

    public void shipOrder(Long orderId) {

        Order order = findById(orderId);

        order.ship();

        orderRepository.save(order);

        order.getNewEvents().forEach(eventPublisher::publish);

        // Diğer işlemler

    }

}

Bu yapı, her sipariş durum değişikliğinin bir olay olarak saklanmasını ve daha sonra bu olayların sorgulanmasını veya farklı hedeflere (örneğin, bir mesaj kuyruğuna) yayınlanmasını sağlar.


Event Sourcing, karmaşık iş süreçlerinin ve denetim gereksinimlerinin olduğu durumlarda çok yararlı olabilir. Bununla birlikte, olayların tutarlı bir şekilde yayınlanması ve saklanması gibi bazı zorluklar da getirir.

Hüseyin Babal - Microservices Patterns Kitabı Okuma Seansları (Video)- Türkçe

Hüseyin Babal Yotutube kanalında Chris Richardson'un Microservices Patterns kitabını okuyor ve yorumluyor.

Kitabı buradan temin edebilirsinsiniz.


















Rastgele İçerik

© tüm hakları saklıdır
made with by templateszoo