एक समाचार पत्र सदस्यता सेवा भाग 2 | - | सेवाओं को फिर से संशोधित करना
परिचय
में इस श्रृंखला का भाग 1 मैंने आपको दिखाया है कि कैसे मैं एक नया Newsletter सदस्यता पृष्ठ बनाया।
परियोजनाएं
मूलतः मेरे पास केवल एक ही एकल परियोजना थी, जो वेबसाइट के लिए सभी कोड को समाहित करती है। इसके अतिरिक्त अनुसूचक सेवा का विभाजन मेरे लिए तर्कसंगत है क्योंकि मैं इसे एक अलग डॉकर कन्टेनर के रूप में विस्थापित कर सकता हूँ जो मुझे बिना अनुसूचितकर्ता को पुनः आरंभ करने के बिना वेबसाइट को अद्यतन करने की अनुमति देता है
इसे करने के लिए मैंने तर्कसंगत रूप से चिंताओं को समूहित किया है 5 परियोजनाएं. यह ASP में एक आम दृष्टिकोण है
अब मैं इन में से प्रत्येक के लिए परीक्षण परियोजना जोड़ सकता है और उन्हें अलग-अलग परीक्षण कर सकते हैं
अधिक स्पष्ट
यह परियोजना एक ASP है
सर्वाधिक स्पष्ट.Dbसंबंध
यह मेरी मुख्य संदर्भ परिभाषा है जिसका उपयोग डाटाबेस के साथ आदान-प्रदान के लिए किया जाता है
सर्वाधिक स्पष्ट सेवाएँ
यह मुख्य वर्ग लाइब्रेरी परियोजना है जो सभी सेवाओं को रखता है जो डाटाबेस के साथ बातचीत करता है
सर्वाधिक स्पष्ट.Shared
यह एक वर्ग लाइब्रेरी परियोजना है जो सभी साझा मॉडलों को रखता है, जो दोनों Mostlylucid और Mostly Lucid के द्वारा उपयोग किए जाते हैं
सर्वाधिक स्पष्ट.अनुसूचक सेवा
यह एक वेब अनुप्रयोग है जो हंगफायर सेवा को नियंत्रित करता है जो अनुसूचित कार्यों के साथ ही ईमेल भेजने के लिए अंतबिंदुओं को चलाता है
इसका ढांचा नीचे दिखाया गया है आप देख सकते हैं कि SchedulerService और मुख्य Mostlylucid केवल आरंभिक सेटअप के लिए DbContext वर्ग का उपयोग करते हैं
graph LR Mostlylucid[Mostlylucid] --> |Intialize| Mostlylucid_DbContext[Mostlylucid.DbContext] Mostlylucid[Mostlylucid] --> Mostlylucid_Shared[Mostlylucid.Shared] Mostlylucid[Mostlylucid] --> Mostlylucid_Services[Mostlylucid.Services] Mostlylucid_DbContext[Mostlylucid.DbContext] --> Mostlylucid_Shared[Mostlylucid.Shared] Mostlylucid_Services[Mostlylucid.Services] --> Mostlylucid_DbContext[Mostlylucid.DbContext] Mostlylucid_Services[Mostlylucid.Services] --> Mostlylucid_Shared[Mostlylucid.Shared] Mostlylucid_SchedulerService[Mostlylucid.SchedulerService] --> Mostlylucid_Shared[Mostlylucid.Shared] Mostlylucid_SchedulerService[Mostlylucid.SchedulerService] --> |Intialize|Mostlylucid_DbContext[Mostlylucid.DbContext] Mostlylucid_SchedulerService[Mostlylucid.SchedulerService] --> Mostlylucid_Services[Mostlylucid.Services]
जैसा कि आमतौर पर मैं डाटाबेस स्थापित करने के लिए प्रविष्टि बिंदु के रूप में एक विस्तार विधि का उपयोग करता हूँ
public static class Setup
{
public static void SetupDatabase(this IServiceCollection services, IConfiguration configuration,
IWebHostEnvironment env, string applicationName="mostlylucid")
{
services.AddDbContext<IMostlylucidDBContext, MostlylucidDbContext>(options =>
{
if (env.IsDevelopment())
{
options.EnableDetailedErrors(true);
options.EnableSensitiveDataLogging(true);
}
var connectionString = configuration.GetConnectionString("DefaultConnection");
var connectionStringBuilder = new NpgsqlConnectionStringBuilder(connectionString)
{
ApplicationName = applicationName
};
options.UseNpgsql(connectionStringBuilder.ConnectionString);
});
}
}
इस विधि को बुलाने के अलावा कोई भी शीर्ष स्तर परियोजना DbContext परियोजना पर कोई निर्भरता नहीं है
नोट: DbContext आरंभ करने के दौरान एक रेस स्थिति से बचने के लिए यह महत्वपूर्ण है ' विशेष रूप से यदि आप माइग्रेशन चलाते हैं तो ) इसे करने के लिए यहाँ मैं सिर्फ अपने Docker Compose फ़ाइल में एक निर्भरता जोड़ा था ताकि सुनिश्चित किया जा सके कि मुख्य Mostlylucid परियोजना मेरे अनुसूचक सेवा प्रारंभ करने से पहले चालू है।
depends_on:
- mostlylucid
healthcheck:
test: ["CMD", "curl", "-f", "http://mostlylucid:80/healthy"]
interval: 30s
timeout: 10s
retries: 5
आप इसे अन्य तरीके से भी पूरा कर सकते हैं रेडिस एक लॉक फ्लैग सेट करने के लिए, जो प्रत्येक इंस्टैंस माइग्रेशन चलाने से पहले जाँच करता है / अन्य कार्य सुनिश्चित करते हैं कि एक समय में केवल एक इंस्टींस चला जा सकता है
बेशक रेडिस के कई अन्य उपयोग भी हैं, विशेष रूप से ASP के साथ IDistributedCache इंटरफेस जो आपको एक कैश प्रदाता के रूप में रेडिस का उपयोग करने की अनुमति देता है टैग आधारित समाप्ति के साथ
हैंगफायर सेटअप
मैंने अपने scheduling को संभालने के लिए Hangfire का उपयोग करना चुना है क्योंकि इसका ASP .NET Core के साथ सुविधाजनक एकीकरण है और उपयोग करने में आसान है
इसके लिए मैं अनुसूचक सेवा परियोजना में निम्नलिखित NuGet पैकेजों को जोड़ा
dotnet add package Hangfire.AspNetCore
dotnet add package Hangfire.PostgreSql
इसका मतलब है कि मैं निर्धारित कार्यों के लिए Postgres को मेरे भंडार के रूप में उपयोग कर सकता हूँ
अब मेरे पास हैंगफायर सेटअप है मैं अपने अनुसूचित कार्यों को जोड़ने के लिए शुरू कर सकता हूँ
public static class JobInitializer
{
private const string AutoNewsletterJob = "AutoNewsletterJob";
private const string DailyNewsletterJob = "DailyNewsletterJob";
private const string WeeklyNewsletterJob = "WeeklyNewsletterJob";
private const string MonthlyNewsletterJob = "MonthlyNewsletterJob";
public static void InitializeJobs(this IApplicationBuilder app)
{
var scope= app.ApplicationServices.CreateScope();
var recurringJobManager = scope.ServiceProvider.GetRequiredService<RecurringJobManager>();
recurringJobManager.AddOrUpdate<NewsletterSendingService>(AutoNewsletterJob, x => x.SendNewsletter(SubscriptionType.EveryPost), Cron.Hourly);
recurringJobManager.AddOrUpdate<NewsletterSendingService>(DailyNewsletterJob, x => x.SendNewsletter(SubscriptionType.Daily), "0 17 * * *");
recurringJobManager.AddOrUpdate<NewsletterSendingService>(WeeklyNewsletterJob, x => x.SendNewsletter(SubscriptionType.Weekly), "0 17 * * *");
recurringJobManager.AddOrUpdate<NewsletterSendingService>(MonthlyNewsletterJob, x => x.SendNewsletter(SubscriptionType.Monthly), "0 17 * * *");
}
}
यहाँ आप देख सकते हैं मैं प्रत्येक के लिए एक काम है SubscriptionType जो एक फ्लैग है कि कितनी बार समाचार पत्र भेजा जाता है निर्धारित करता है Cron कार्य की आवृत्ति सेट करने के लिए वर्ग.
चूंकि मेरे पास मासिक और साप्ताहिक दोनों सदस्यताओं के लिए दिन का विकल्प है, इसलिए मैं समय 17:00 (5 pm | ) को सेट करता हूँ क्योंकि यह समाचार पत्र भेजने का अच्छा समय है। मैं एक काम भी करता हूँ जो हर घंटे चलता है कि उन लोगों को समाचार पत्र भेजने के लिए, जिन्होंने प्रत्येक पोस्ट में सदस्यता ली है
यह फिर एक में बुलाता है SendNewsletter विधि में NewsletterSendingService वर्ग. Which I'll go into more detail in a later post
डीटोस और मानचित्रण
मैं उपयोग करता हूँ डीटीओ मेरे अनुप्रयोग की परतों के बीच डेटा ले जाने के लिए . यह जटिलता जोड़ता है क्योंकि मुझे नक्शे बनाने की आवश्यकता होती है | ( | प्रायः दो बार |) | Entities को Dtos में और फिर Dtos को ViewModels पर \ ( |and back |
आप इस मैपिंग करने के लिए AutoMapper जैसे दृष्टिकोणों का उपयोग कर सकते हैं / Mapster आदि क्वेरी योग्य विस्तार विधि जो आपको क्वेरी में डीटो के लिए सीधे नक्शे बनाने की अनुमति देता है
लेकिन इस मामले में मैंने सिर्फ मैपर एक्सटेंशन जोड़ने का निर्णय किया जहां मैं आवश्यक था
public static class BlogPostEntityMapper
{
public static BlogPostDto ToDto(this BlogPostEntity entity, string[] languages = null)
{
return new BlogPostDto
{
Id = entity.Id.ToString(),
Title = entity.Title,
Language = entity.LanguageEntity?.Name,
Markdown = entity.Markdown,
UpdatedDate = entity.UpdatedDate.DateTime,
HtmlContent = entity.HtmlContent,
PlainTextContent = entity.PlainTextContent,
Slug = entity.Slug,
WordCount = entity.WordCount,
PublishedDate = entity.PublishedDate.DateTime,
Languages = languages ?? Array.Empty<string>()
};
}
}
यहाँ आप देख सकते हैं मैं मानचित्र BlogPostEntity मेरे मुख्य BlogPostDto जो मेरा स्थानांतरण वस्तु है
लक्ष्य यह है कि फ्रंट एंड सेवाएँ अस्तित्व वस्तु के बारे में कुछ नहीं जानते हैं और आधारभूत डेटा संरचना से वंचित हैं
इन उच्च स्तरीय सेवाओं में मैं इन Dtos को दृश्य मॉडलों पर नक्शे बनाने के लिए कोड है जो बाद में नियंत्रकों में उपयोग किया जाता है
public static PostListModel ToPostListModel(this BlogPostDto dto)
{
return new PostListModel
{
Id = dto.Id,
Title = dto.Title,
Language = dto.Language,
UpdatedDate = dto.UpdatedDate.DateTime,
Slug = dto.Slug,
WordCount = dto.WordCount,
PublishedDate = dto.PublishedDate,
Languages = dto.Languages
};
}
public static BlogPostViewModel ToViewModel(this BlogPostDto dto)
{
return new BlogPostViewModel
{
Id = dto.Id,
Title = dto.Title,
Language = dto.Language,
Markdown = dto.Markdown,
UpdatedDate = dto.UpdatedDate.DateTime,
HtmlContent = dto.HtmlContent,
PlainTextContent = dto.PlainTextContent,
Slug = dto.Slug,
WordCount = dto.WordCount,
PublishedDate = dto.PublishedDate,
Languages = dto.Languages
};
}
एक मापर उपकरण का उपयोग करके यह बॉयलप्लेट कोड से बच सकता है (और त्रुटि कम कर सकते हैं, लेकिन मैं यह दृष्टिकोण मेरे लिए इस उदाहरण में अच्छा काम करता है
अब मैं इन सभी मॉडल सेटअप है मैं उन्हें प्रयोग करने के लिए कंट्रोलर विधि जोड़ने शुरू कर सकता हूँ मैं इस प्रवाह को अगले भाग में कवर करेंगे
अंत में
अब हमारे पास तंत्र है जो हमें अपने उपयोगकर्ताओं को newsletter सदस्यता प्रदान करने के लिए शुरू करने की अनुमति देता है यह रीफॅक्टरिंग थोड़ा दर्दनाक था लेकिन यह-' अधिकांश परियोजनाओं के लिए जीवन का एक तथ्य है। भावी पोस्ट्स इस ',' के बाकी को शामिल करेंगे जिसमें मेरी उपयोग पर एक अनुवर्तन [फ्लुएंटमेल] ईमेल और होस्टेड सेवा भेजने के लिए