Orijinal yayım hafiz.dev
Birkaç yıl önce, 28 metoda sahip bir UserService geliştirdim. Bu servis kaydolma, e-posta doğrulama, şifre sıfırlama, abonelik yükseltme, profil güncellemeleri ve hesap silme işlemlerini yönetiyordu. Kod tabanındaki en çok kullanılan sınıf ve üzerinde değişiklik yapması en tehlikeli olanıydı. Kayıt akışında yapılan bir değişiklik, 400 satırı aşan alakasız kodu gözden geçirmek anlamına geliyordu.
Bir noktada, Laravel’deki bir servis sınıfının gerçek tanımının ne olduğunu kendime sordum. php artisan make:service komutu yok. Hiçbir arayüz gerekliliği yok. Framework dokümantasyonunda neyin burada yer alması gerektiğine dair bir kılavuz yok. Mantığı kontrolörlerden ayırmamız için birinin bize bunu söylediğinde, mantığımızı servislerde toplamak için ilk öğrendiğimiz desenler bunlardı.
Sonuç üzerinde tahmin edilebilir. Kontrolör ince hale geldi. Servis şişti. Problemi çözmedik, sadece oradan oraya taşıdık.
Bu yazı, başlangıçta sahip olmayı dilediğim karar ağacı.
Üç desen, kısaca
Üç desen, kısaca
Ağaçtan önce, her desenin ne yaptığını hızlıca anlamak için, çünkü toplulukta dil tutarsız.
Service sınıfı, aynı alan nesnesindeki ilişkili işlemleri gruplar. Bir SubscriptionService, bir kullanıcıyı abone yapma, yükseltme, iptal etme ve mevcut planını kontrol etme işlerini bilmektedir. Tek bir işlemle sınırlı değildir. Bunu “aboneliklerle ilgili her şey” sınıfı olarak düşünün. Statefull ya da stateless olabilir.
Action sınıfı tek bir atomik işlemi yönetir. CreateUser, SendPasswordResetEmail, ChargePaymentMethod. Bir sınıf, bir iş, bir kamu metodu. Sonucu hemen ihtiyaç duymadığınız sürece bir Job olamaz. Kontrolörden, bir Artisan komutundan, başka bir sınıftan ve testten kullanılabilir. Anahtar kelime bağlamlar arasında yeniden kullanım.
Job, kuyrukta asenkron olarak çalışır. Tamam. Tanım bu. Eğer kodunuzun arka planda çalışmasına gerek yoksa, bu bir Job değildir; ne kadar cazip görünüyorsa görünsün. Jobs, HTTP yanıtı dönmeden önce sonuca ihtiyacınız olmadığında ateş et ve unut tarzı işler içindir.
Bazı örtüşmeler var. Ama örtüşme, seçim yapmanız gerektiğinin işareti; ikisi de eşdeğer değildir.
Karar ağacı
Karar ağacı
Herhangi bir işlemle geçin ve bir yanıt alırsınız.
Ağaç göz ardı edildiğinde ne yanlış gider
Ağaç göz ardı edildiğinde ne yanlış gider
Şişmiş Service. Kullanıcı kaydı için bir UserService oluşturuyorsunuz. Altı ay sonra birisi buna şifre sıfırlamayı ekliyor, çünkü sonuçta kullanıcı ile ilgili. Sonra profil güncellemeleri. Sonra hesap silme. Sonra bir kullanıcının indirim için uygun olup olmadığını kontrol eden bir metod. Artık servisiniz, tutarlı bir kimliği olmayan 600 satırlık bir sınıf haline geldi. Kayıt için her test, istemediğiniz tüm abonelik mantığını içeren tüm sınıfı alıyor.
Düzeltme: sorumluluğa göre bölmek, modele göre değil. RegistrationService kaydı yönetir. PasswordResetService şifre sıfırlamaları ile ilgilidir. Ya da, bu işlemler basit ve atomikse, onları Action’lar olarak yapın.
Action klasör patlaması. Zıt problem. Action desenini benimsiyorsunuz ve her bir şey için bir Action oluşturuyorsunuz. GetUserByEmailAction. FormatDateAction. ValidatePostcodeAction. 200 dosyadan sonra, app/Actions içindeki gezinmek, kontrolöre bakmaktan daha yavaş hale geliyor.
Actions net bir soruya cevap vermelidir: “Bunu bir kontrolörden ve bir Artisan komutundan ve bir kuyruk job’undan çağırmak ister miyim?” Cevap hayırsa, içinde tutun.
Jobs, abartılı Actions olarak kullanılıyor. En yaygın hata. Bazı mantıklarınız ağır geliyor, bu yüzden bir Job oluşturuyorsunuz. Ama onu dispatch()->now() ile senkronize bir şekilde yayımlıyorsunuz, ya da sonuç değerine ihtiyacınız olduğunu anlıyorsunuz, bu yüzden geçmeye çalışıyorsunuz. Daima senkronize olarak gönderdiğiniz ve sonucuna ihtiyacınız olan bir Job, yanlış kostüm giymiş bir Action’dır. Bir Action yapın.
Üretimden gerçek örnekler
Üretimden gerçek örnekler
Ağacın nasıl işlediğini gösteren bazı senaryolar.
Bir aboneliği şarj etme. Asenkron mu? Hayır. Sonucu bilmeniz gerekiyor, böylelikle kullanıcıya erişim vermeniz lazım. Tek operasyon mu? Evet. Birden fazla yerde mi kullanılıyor (web ödeme, API ödeme, CLI seeder)? Evet. Bu bir Action: ChargeSubscription::handle($user, $plan).
50,000 kullanıcıya haftalık bir dergi e-postası gönderme. Asenkron mı? Evet. Job. Kullanıcıları parçalara ayırırsınız, her batch için bir Job gönderirsiniz, geçersiniz. Job, Mailable’i çağırır. HTTP yanıtında sonuca ihtiyacınız yok.
Bir aboneliğin nasıl çalıştığıyla ilgili her şey. Abone olma, iptal etme, durumu kontrol etme, promosyon kodu uygulama, Stripe’dan gelen webhook’u yönetme. Bunlar, aynı alan kavramı üzerine yapılan birden fazla işlem ve ortak mantık (değişiklik yapmadan önce mevcut durumu kontrol etme) içerir. Bu bir Service’dir: SubscriptionService. Her metot bir geçişi yönetir, ancak hepsinin birbirini bilmesi gerekir.
Bireysel bir işlem e-postası gönderme. Asenkron mı? Evet, muhtemelen. Ama Mail::queue() bunu halleder. Bunun etrafında bir Job sınıfı sarmaya gerek yok. Mailable kendisi, Mail::queue() ile gönderildiğinde kuyruklama işlemini halleder. Bu, ekstra sınıfın katman eklediği ancak değer eklemediği örneklerden biridir.
İnsanların tartıştığı kısım
İnsanların tartıştığı kısım
Bu tartışmanın dürüst versiyonu, Service ve Action’in genelde bir takım tercihi olduğu daha çok teknik bir gereklilikten ziyade. İkisi de işe yarar. Gerçek risk, her şeyi tek bir desenle uygulamaktır; bu durum uygunluk bakımından tehlikeli olabilir.
Bir alanla ilgili bir çok operasyon paylaşan durumlarda Services kullanıyorum. Tek bir işlemim olduğunda ve birden fazla yerden çağıracağımı bildiğimde Actions kullanıyorum. Eğer mantık yeterince basitse, bunun içinde kalmasını beklemiyorum. O zaman ikisini de kullanmam.
Kendi projelerimde uyguladığım kural: Eğer bir sınıf 5’ten fazla metoda veya 150 satırdan fazla koda sahipse, bölünmesi veya yeniden gözden geçirilmesi gerekiyor. Bu üst sınır, “Bu sınıf tam olarak ne yapıyor?” sorusunu sormaya zorlar.
Asenkron desenler üzerine derinlemesine bir inceleme ve Jobs’ın daha büyük bir kuyruk mimarisiyle nasıl uyum sağladığını öğrenmek için, kuyruk işleyicileri rehberi işçi boyutlandırma, yeniden deneme ve ölçeklendirme sırasında bilmeniz gereken üretim kurulumları hakkında bilgi verir.
Sıkça Sorulan Sorular
Sıkça Sorulan Sorular
Yeni kod için her zaman Action mı kullanmalıyım?
Her zaman değil. Actions, tek giriş noktasına sahip atomik, stateless işlemler için en iyi şekilde işler. Bir alan oluşturuyorsanız (abonelikler, faturalama, bildirimler) ve birden fazla işlem bağlamı paylaşacaksa, bir Service daha temizdir. Sorulması gereken soru: bu sınıf tek bir şey mi yapıyor yoksa bir kavram hakkında her şeyi mi biliyor?
Actions dizin yapısında nerede bulunmalı?
app/Actions/. Daha büyük projeler için alan adına göre daha fazla ad alanı oluşturun: app/Actions/Billing/ChargeSubscription.php. İsimleri fiil-isim çiftleri olarak tutun: CreateUser, SendPasswordReset. UserAction gibi adlardan kaçının. Bu sadece farklı bir ek ile bir Service’dir.
lorisleiva/laravel-actions paketi ne olacak?
İyi bir paket. Bir Action sınıfının bir kontrolör, bir Job, bir dinleyici ve bir komut olarak çalışmasına izin verir. Takımınız bu deseni kod tabanı genelinde benimserse dikkate değer. Actions ve Services için test yaparken, Pest 4 test rehberi kelepçeleme desenlerini kapsamlı incelemeye alır. Birden fazla bağlamda çalışması gereken Actions için gerçek değer katar; ancak daha basit projelerde ek yük getirir.
Bir Job bir Action’ı çağırabilir mi?
Evet, ve bu genellikle doğru bir modeldir. Job, kuyruk mekanizmasını (yeniden denemeler, gecikmeler, geri dönüşler) yönetir. Action ise gerçek mantığı yönetir. ProcessSubscriptionRenewal::handle() işleri yayımlar, ChargeSubscription::handle()‘ı çağırır ve kuyruk için özel başa çıkma durumlarını yönetir. Temiz bir ayrım.
Olaylar ve Dinleyiciler hakkında ne düşünüyorsunuz?
Olaylar ve Dinleyiciler, bir şeyler olduktan sonraki ayrıştırılmış yan etkiler içindir. Olaylar, Dinleyiciler ve Gözlemciler rehberi desenleri derinlemesine incelemektedir. Kullanıcı kaydedildiğinde, UserRegistered tetikleyin ve dinleyicilerin hoş geldin e-postasını, oryantasyon dizisini, analitik olayını yönetmesine izin verin. Bunu Action’lar veya Services için kullanmayın. Bu, Events sisteminin işidir. İlişki şudur: Actions ve Services, bir şeylerin olmasına sebep olur; Events, olanları iletmek için kullanılır.
Gerçek çıkarım
Gerçek çıkarım
Seçtiğiniz desen, tutarlı bir şekilde uygulamak ve ne zaman bu desenin dışına çıkacağını bilmekten daha az önemlidir. Service sınıfları yanlış değildir. Actions her zaman daha iyi değildir. Jobs ise, kuyrukla süslenmiş senkron mantık için uygun değildir.
Bir sınıf fazla şey yapmaya başladığında, bu ağaç için bir sinyal vermek demektir.
Karmaşık bir kod tabanı ile ilgili sorun yaşıyorsanız, iletişime geçin.
Kaynak: Orijinal Makale


