[*]
charset = utf-8
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
[*.cs]
# Nullable reference types
dotnet_diagnostic.CS8600.severity = error
dotnet_diagnostic.CS8601.severity = error
dotnet_diagnostic.CS8602.severity = error
dotnet_diagnostic.CS8603.severity = error
dotnet_diagnostic.CS8604.severity = error
# Code style rules
dotnet_style_qualification_for_field = false:warning
dotnet_style_qualification_for_property = false:warning
dotnet_style_qualification_for_method = false:warning
dotnet_style_qualification_for_event = false:warning
# Naming conventions
dotnet_naming_rule.interface_should_begin_with_i.severity = error
dotnet_naming_rule.interface_should_begin_with_i.symbols = interface
dotnet_naming_rule.interface_should_begin_with_i.style = begins_with_i
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.capitalization = pascal_case
# Async methods should end with Async
dotnet_naming_rule.async_methods_end_in_async.severity = error
dotnet_naming_rule.async_methods_end_in_async.symbols = any_async_methods
dotnet_naming_rule.async_methods_end_in_async.style = end_in_async
dotnet_naming_symbols.any_async_methods.applicable_kinds = method
dotnet_naming_symbols.any_async_methods.applicable_accessibilities = *
dotnet_naming_symbols.any_async_methods.required_modifiers = async
dotnet_naming_style.end_in_async.required_suffix = Async
dotnet_naming_style.end_in_async.capitalization = pascal_case
شما میتوانید این فایل را مطابق سلیقهی تیم خودتان شخصیسازی کنید. نکتهی کلیدی این است که از ابتدا روی استانداردها به توافق برسید و آنها را بهصورت خودکار enforce کنید. 🔒
با استفاده از editorconfig. ، کد reviewها سریعتر میشوند چون دیگر لازم نیست توسعهدهندگان دربارهی فرمتبندی یا naming convention بحث کنند. ابزارها این قوانین را بهصورت خودکار اعمال میکنند. ⚙️
4️⃣ Centralize Package Management 📦
با بزرگتر شدن solution، مدیریت نسخهی پکیجهای NuGet در چندین پروژه سخت میشود. پروژههای مختلف در نهایت از نسخههای متفاوت یک پکیج استفاده میکنند که میتواند باعث مشکلات سازگاری شود و آپدیتها را سختتر کند. Central Package Management (CPM) این مشکل را با مدیریت همهی نسخههای پکیج در یک مکان حل میکند.
شما باید یک فایل Directory.Packages.props بسازید که تمام نسخههای پکیج برای solution را تعریف میکند. این فایل را در همان دایرکتوری .sln قرار دهید:
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<!-- Web -->
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0" />
<!-- Database -->
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="10.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.0" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.0" />
<!-- Testing -->
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<!-- Code Analysis -->
<PackageVersion Include="Meziantou.Analyzer" Version="2.0.257" />
<PackageVersion Include="SonarAnalyzer.CSharp" Version="10.16.0.128591" />
<PackageVersion Include="Roslynator.Analyzers" Version="4.14.1" />
<PackageVersion Include="xunit.analyzers" Version="1.26.0" />
</ItemGroup>
</Project>
برای فعال شدن CPM باید property زیر را در این فایل اضافه کنید:ManagePackageVersionsCentrally
وقتی CPM فعال باشد، فایلهای پروژه پکیجها را بدون مشخص کردن نسخه ریفرنس میکنند:
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" />
</ItemGroup>
در صورت نیاز، میتوانید نسخهی یک پکیج را در یک پروژهی خاص override کنید:
<ItemGroup>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.1" />
</ItemGroup>
5️⃣سادهسازی توسعهی لوکال با Aspire 🚀
راهاندازی محیط توسعهی لوکال معمولاً آزاردهنده است. توسعهدهندهها ساعتها زمان صرف نصب دیتابیسها، تنظیم connection stringها و هماهنگ کردن وابستگیها با هم میکنند. 😵💫
حتی با وجود Docker، باز هم مقدار زیادی تنظیمات دستی وجود دارد؛ از جمله volumeها و کانفیگ هر کانتینر.
ءNET Aspire. این مشکل را با ارائهی یک روش یکپارچه برای مدیریت وابستگیها، پیکربندی و استقرار (deployment) حل میکند.
ءAspire یک application framework برای ساخت اپلیکیشنهای cloud-native، قابل مشاهده (observable) و آمادهی production با NET. است. این فریمورک بهصورت پیشفرض موارد زیر را هندل میکند:
• Service discovery
• Configuration
• Health checks
• Telemetry 📊
از نسخهی 13 به بعد، NET Aspire. به Aspire تغییر برند داد و حالا یک پلتفرم چندزبانه (multi-language) برای ساخت اپلیکیشن است.
ءAspire دو نوع پروژهی جدید به solution شما اضافه میکند:
🔸️ءAppHost: ارکستریتوری که معماری اپلیکیشن و وابستگیها را تعریف میکند.
🔹️ءServiceDefaults: تنظیمات مشترک و observability که روی همهی سرویسها اعمال میشود.
نحوهی اضافه کردن Aspire به پروژهی جدید
مرحله 1️⃣: نصب templateهای Aspire
dotnet new install Aspire.ProjectTemplates
مرحله 2️⃣: نصب Aspire CLI
dotnet tool install --global aspire.cli
مرحله 3️⃣: اضافه کردن Aspire به solution از طریق Visual Studio یا Rider، یا استفاده از CLI برای ساخت یک پروژهی جدید Aspire.
مرحله 4️⃣: تعریف وابستگیها در پروژهی AppHost
var builder = DistributedApplication.CreateBuilder(args);
var cache = builder.AddRedis("cache");
var postgres = builder.AddPostgres("postgres");
builder
.AddProject<Projects.Products_Api>("products-api");
.WithReference(cache)
.WithReference(postgres);
builder.Build().Run();
مرحله 5️⃣: استفاده از connection stringها در پروژهی API
var postgresConnection = configuration.GetConnectionString("postgres");
var redisConnection = configuration.GetConnectionString("cache");ءAspire بهصورت خودکار connection stringها را از طریق environment variableها تزریق میکند. نیازی نیست آنها را دستی داخل appsettings.json تعریف کنید. ⚡️
با یکپارچگی Docker، وقتی پروژهی AppHost را اجرا میکنید، Aspire تمام وابستگیها را بهصورت خودکار بالا میآورد. PostgreSQL، Redis و هر سرویس دیگری داخل کانتینر اجرا میشوند و Aspire چرخهی عمر آنها را مدیریت میکند. 🐳
داشبورد Aspire در مرورگر باز میشود و همهی سرویسهای در حال اجرا، وضعیت سلامت، لاگها و distributed traceها را نمایش میدهد. 📈
مزایای کلیدی استفاده از Aspire
🔸️Simplified local development:
اجرای تمام سرویسها و دیتابیسها با یک دستور واحد.
🔹️Built-in observability:
یکپارچگی آماده با OpenTelemetry برای لاگ، متریک و تریس.
🔸️Service discovery:
سرویسها بدون URLهای هاردکد شده همدیگر را پیدا میکنند.
🔹️Configuration management:
پیکربندی متمرکز برای لوکال و کلاود.
🔸️Easy deployment:
استقرار کل سیستم روی Docker، Azure یا AWS با کمترین تنظیمات.
🔹️Consistent developer experience:
اعضای جدید تیم میتوانند کل سیستم را در چند دقیقه اجرا کنند. 🎯
6️⃣ راهاندازی OpenTelemetry 🔍
درک اینکه داخل اپلیکیشن شما دقیقاً چه اتفاقی میافتد، حیاتی است. OpenTelemetry یک روش استاندارد برای جمعآوری و تحلیل دادههای telemetry از اپلیکیشنها فراهم میکند و به شما دید (visibility) دقیقی نسبت به رفتار سیستم میدهد. 👁
هر اپلیکیشن وابستگیهای خارجی دارد؛ مثل دیتابیسها، cacheها، APIها و سایر سرویسها. مانیتور کردن این وابستگیها برای درک عملکرد اپلیکیشن و نحوهی تعامل آن با بقیهی سیستم ضروری است. ⚙️
ءOpenTelemetry سه نوع داده از اپلیکیشن جمعآوری میکند:
• ءLogs توضیح میدهند چه اتفاقی افتاده است. 📝
• ءTraces نشان میدهند عملیات کجا انجام شده و چقدر زمان برده است. ⏱️
• ءMetrics نشان میدهند رویدادها با چه فرکانسی رخ میدهند. 📊
وقتی از Aspire استفاده میکنید، OpenTelemetry از قبل برای شما پیکربندی شده است. پروژهی ServiceDefaults شامل تنظیمات observability است:
public static TBuilder ConfigureOpenTelemetry<TBuilder>(this TBuilder builder)
where TBuilder : IHostApplicationBuilder
{
builder.Logging.AddOpenTelemetry(logging =>
{
logging.IncludeFormattedMessage = true;
logging.IncludeScopes = true;
});
builder.Services.AddOpenTelemetry()
.WithMetrics(metrics =>
{
metrics.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddRuntimeInstrumentation();
})
.WithTracing(tracing =>
{
tracing.AddSource(builder.Environment.ApplicationName)
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation();
});
builder.AddOpenTelemetryExporters();
return builder;
}
ءAspire بهصورت خودکار OpenTelemetry را طوری تنظیم میکند که logs، metrics و traces را به داشبورد Aspire ارسال کند.
اما Aspire دادههای OpenTelemetry و داشبورد را ذخیره (persist) نمیکند. در محیط production، به یک راهحل قویتر برای ذخیره و تحلیل دادههای telemetry نیاز دارید. 🏗
میتوانید exporterهای اضافی تنظیم کنید تا دادهها به ابزارهایی مثل Jaeger، Seq، Grafana یا سایر ابزارهای observability ارسال شوند. 📡
بهصورت پیشفرض، OpenTelemetry فقط برای ASP.NET Core و HttpClient فعال است. برای وابستگیهای اضافی مثل PostgreSQL یا Redis، باید پکیجهای instrumentation را نصب کنید:
dotnet add package Npgsql.OpenTelemetry
dotnet add package OpenTelemetry.Instrumentation.StackExchangeRedis
سپس آنها را در ServiceDefaults رجیستر کنید:
tracing.AddSource(builder.Environment.ApplicationName)
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddNpgsql()
.AddRedisInstrumentation();
داشبورد Aspire، distributed traceها را در تمام سرویسها نمایش میدهد. میتوانید دقیقاً ببینید چه کوئریهایی روی دیتابیس اجرا شدهاند، چقدر طول کشیدهاند و bottleneckها کجا هستند. 🧠
شروع observability از همان روز اول به شما کمک میکند ویژگیهای عملکردی سیستم را زودتر درک کنید. میتوانید کوئریهای کند را شناسایی کنید، API callها را بهینه کنید و مشکلات را قبل از رسیدن به production رفع کنید. 🚀
7️⃣ خودکارسازی Build و Test با GitHub Actions 🤖
توسعهی مدرن نرمافزار نیازمند pipelineهای خودکار است. شما نمیتوانید به استقرار دستی یا رد کردن تستهای خودکار متکی باشید. 🚫
یک pipeline خوب CI/CD تضمین میکند که هر تغییر در کد بهصورت یکسان build، test و deploy شود. این کار باگها را قبل از رسیدن به production شناسایی میکند و سرعت تحویل شما را افزایش میدهد. ⚡️
بدون pipeline مناسب CI/CD، زمان زیادی صرف کارهای دستی میکنید و ریسک خطای انسانی بهشدت افزایش پیدا میکند. ⏳
برای توسعهدهندگان NET. که از GitHub استفاده میکنند، GitHub Actions انتخاب طبیعی است. این ابزار بهصورت یکپارچه با ریپازیتوری شما کار میکند و هر چیزی که برای build و test اپلیکیشنهای NET. نیاز دارید را فراهم میکند. 🧩
یک فایل به نام github/workflows/ci.yml. در ریپازیتوری خود بسازید:
name: BUILD_AND_TEST
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
dotnet-version: [ '10.0.x' ]
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: ${{ matrix.dotnet-version }}
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build --no-restore --verbosity normal
- name: Build Docker image with Aspire
if: ${{ success() }}
run: |
dotnet tool install --global aspire.cli
aspire publish -o docker-compose-artifacts
docker compose -f docker-compose-artifacts/docker-compose.yaml build
این pipeline روی هر push به شاخهی main و روی هر pull request اجرا میشود. وابستگیها را restore میکند، solution را build میکند، تستها را اجرا میکند و با استفاده از Aspire یک Docker image میسازد. 🐳
ءAspire CLI یک فایل Docker Compose تولید میکند که شامل تمام سرویسها و وابستگیهای آنهاست. سپس pipeline ایمیجهای Docker را برای استقرار build میکند. 🏗
بهترین روشها برای CI/CD:
• Keep pipelines fast:
هدف زیر ۱۰ دقیقه برای CI builds باشد. ⏱️
• Run tests on every pull request:
مشکلات را قبل از merge شدن به main شناسایی کنید. 🔍
• Store secrets securely:
از GitHub Secrets برای دادههای حساس مثل API key و connection string استفاده کنید. هرگز secret را در ریپو commit نکنید. 🔐
• Fail fast:
اگر تستها fail شدند، pipeline را فوراً متوقف کنید. برای کد خراب Docker image نسازید. ❌
• Use caching:
پکیجهای NuGet و artifactهای build را cache کنید تا اجراهای بعدی سریعتر شوند. ⚡️
8️⃣ قالب پروژه برای شروع 🚀
راهاندازی یک پروژهی جدید NET. با مراحل بالا سخت نیست. اما چیزهای بسیار بیشتری وجود دارد که باید در پروژه تنظیم شوند، مثل:
• Code Structure: Clean Architecture / Vertical Slices / N-Layered
• Authentication و Authorization
• ASP .NET Core Identity
• JWT Claims و Refresh Token
• Logging
• Result Pattern
• Architecture Tests
• Unit Tests و Integration Tests
• ساختار endpointهای WebApi
• یکپارچهسازی با Jaeger و Seq
جمعبندی 🧠
شروع یک پروژهی جدید NET. با فونداسیون درست، در بلندمدت زمان ذخیره میکند و از ایجاد technical debt جلوگیری میکند.
این روشها خیلی سریع در طول عمر پروژه نتیجه میدهند. بهترین زمان برای تعریف استانداردهای کیفیت، ابتدای پروژه است. پروژهی بعدی NET. خود را با این هفت قدم شروع کنید تا نرمافزار بهتری را سریعتر بسازید. 🚀
امیدوارم این مقاله برایتان مفید بوده باشد👋
🔖هشتگها:
#DotNet #SoftwareEngineering
#OpenTelemetry #Aspire #DeveloperExperience
ءSwagger در NET 10. حذف شده؟ چطور دوباره اضافهاش کنیم 🔍
شما یک پروژهی NET 10. میسازید، برنامه را اجرا میکنید، تلاش میکنید صفحهی Swagger را باز کنید، و با خطای 404 Not Found مواجه میشوید. ❌
پس چطور باید آن را برگردانیم؟
چرا Swagger حذف شده است؟ 🤔
مایکروسافت پشتیبانی از Swagger را از NET 9. حذف کرده است. طبق این بحث در GitHub:
"The project is no longer actively maintained by its community owner. Issues have not been addressed or resolved..."
یعنی:
این پروژه دیگر بهصورت فعال توسط مالک جامعهی آن نگهداری نمیشود. مشکلات بررسی یا حل نشدهاند.
مایکروسافت به سمت استفاده از OpenAPI حرکت کرده است، یعنی حالا مستندات OpenAPI میتوانند بدون نیاز به Swagger تولید شوند. 📄
چطور Swagger را به NET 10. اضافه کنیم 🛠
ابتدا باید پکیج زیر را به پروژهی Web API خود اضافه کنید:
Swashbuckle.AspNetCore.SwaggerUI
سپس داخل فایل Program.cs باید پارامتر options را به متد UseSwaggerUI اضافه کنید و endpoint را روی /openapi/v1.json تنظیم کنید.
// Program.cs
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
// Add this
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/openapi/v1.json", "API v1");
});
}
اجرای خودکار Swagger هنگام اجرا 🚀
اگر میخواهید برنامه هنگام اجرا بهصورت خودکار با Swagger باز شود، به مسیر زیر بروید:
Properties > launchSettings.json
پروفایل https را پیدا کنید، مقدار launchBrowser را برابر true قرار دهید و launchUrl را روی swagger تنظیم کنید.
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:5155",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7284;http://localhost:5155",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}جایگزینهای Swagger 🔄
ءSwagger دوباره اضافه شده، اما آیا جایگزینهای بهتری وجود دارند؟
Scalar ✨
ءScalar یک جایگزین برای Swagger است. این ابزار به شما اجازه میدهد endpointها را با یک رابط کاربری متفاوت تست کنید.
برای اضافه کردن Scalar، پکیج Scalar.AspNetCore را در پروژهی Web API خود نصب کنید.
سپس، در فایل Program.cs، فضای نام Scalar.AspNetCore را import کنید و کد زیر را اضافه کنید:
// Program.cs
using Scalar.AspNetCore; // <-- Import this namespace
...
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
app.MapScalarApiReference(); // <-- Add this line
}
برای اجرای برنامه همراه با Scalar، فایل launchSettings.json را باز کنید.
مقدار launchBrowser را روی true بگذارید و launchUrl را برابر scalar/v1 تنظیم کنید.
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:5155",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "scalar/v1",
"applicationUrl": "https://localhost:7284;http://localhost:5155",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}همچنین میتوانید تنظیمات Scalar را شخصیسازی کنید. در این مثال، عنوان روی "My API" تنظیم شده، تم برابر با ScalarTheme.Mars است و نوار کناری مخفی شده است.
// Program.cs
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
app.MapScalarApiReference(options =>
{
options.WithTitle("My API");
options.WithTheme(ScalarTheme.Mars);
options.HideSidebar();
});
}
Redoc 📘
ءRedoc مستندات API را تولید میکند، اما اجازهی تست endpointها را نمیدهد.
برای اضافه کردن Redoc، پکیج Redoc.AspNetCore را در پروژهی Web API خود نصب کنید.
سپس کد زیر را به Program.cs اضافه کنید:
// Program.cs
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
app.UseReDoc(options =>
{
options.RoutePrefix = "docs";
options.SpecUrl = "/openapi/v1.json";
});
}
برای اجرای برنامه همراه با Redoc، فایل launchSettings.json را باز کنید.
مقدار launchBrowser را روی true بگذارید و launchUrl را برابر با docs تنظیم کنید (یا هر مقداری که در options.RoutePrefix گذاشتهاید).
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:5155",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "docs",
"applicationUrl": "https://localhost:7284;http://localhost:5155",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}جایگزینها بهجای استفاده از UI 🧩
ممکن است نخواهید یک رابط کاربری (UI) شامل تمام endpointهای خود را در دسترس قرار دهید.
فایل http. 📄
وقتی یک Web API میسازید، یک فایل http. بهصورت پیشفرض ایجاد میشود. اگر از Visual Studio استفاده میکنید، این فایل به شما اجازه میدهد endpoint پیشفرض WeatherForecast را تست کنید. وقتی برنامه در حال اجراست، میتوانید درخواستها را ارسال کنید یا آنها را مستقیماً دیباگ کنید.
در Visual Studio، اگر بخواهید endpointهای بیشتری از برنامهی خود را به فایل http. اضافه کنید، به مسیر زیر بروید:
View > Other Windows > Endpoints Explorer
این بخش لیستی از تمام endpointهای موجود در Web API شما را نمایش میدهد.
با راستکلیک روی یک endpoint و انتخاب گزینهی Generate request،
ءVisual Studio آن درخواست را به فایل http. اضافه میکند.
Postman 📬
گزینهی دیگر استفاده از Postman است. شما میتوانید endpointها را بهصورت دستی اضافه کنید یا یک سند OpenAPI را ایمپورت کنید.
برای ایمپورت کردن یک سند OpenAPI، برنامهی خود را اجرا کنید و به مسیر زیر بروید:
/openapi/v1.json
آدرس URL را کپی کنید.
در Postman، به بخش Import بروید و URL را Paste کنید.
ءPostman سپس تمام endpointها را ایمپورت میکند تا بتوانید آنها را تست کنید. 🚀
🔖هشتگها:
#DotNet #OpenAPI #Postman #HttpFile #WebAPI #ASPNetCore #DeveloperTools
معرفی کتابها 📚
ء"Head First Design Pattern" نوشتهی Eric Freeman و Elisabeth Robson یک کتاب کلاسیک است که در سال 2004 منتشر شده و دربارهی design patternهای نرمافزاری است.
بعد از انتشار، این کتاب بهخاطر سادگی و سبک ارائهی سرگرمکنندهاش بسیار محبوب شد. این کتاب با استفاده از تصاویر قوی و روایت داستانی، مفاهیم پیچیدهای مثل design pattern را ساده و قابلفهم میکند. همچنین برخلاف بسیاری از کتابهای دیگر، فقط 13 تا 15 design pattern پرکاربرد را پوشش میدهد. این کتاب بسیار beginner friendly است برای یادگیری design patternها. واقعاً دوستداشتنی است. 😍
نمای کلی در سطح بالا 🧠
🔹️Head First Design Pattern
هدف این کتاب معرفی design patternها در توسعهی نرمافزار به شکلی جذاب و قابلدسترس است. این کتاب از رویکردی منحصربهفرد استفاده میکند که شامل تصاویر بصری، طنز و مثالهای عملی برای انتقال مؤثر مفاهیم پیچیده است.
مرور کلی کتاب:
• Introduction to Design Patterns:
معرفی design patternها و توضیح اینکه چرا در توسعهی نرمافزار ضروری هستند و چگونه به ایجاد کدی flexible، reusable و maintainable کمک میکنند.
• Understanding Design Principles:
قبل از ورود به patternها، اصولی مثل encapsulation، inheritance و polymorphism توضیح داده میشود.
• Categorization:
ابتدا creational patternها، سپس structural و در نهایت behavioral patternها بررسی میشوند.
• Real-World Examples and Case Studies:
مثالهای واقعی برای کاربرد عملی patternها ارائه میشود.
• Anti-Patterns and Pitfalls:
به anti-patternها و اشتباهات رایج اشاره میشود.
• Design Patterns in Context:
بررسی جایگاه patternها در معماری نرمافزار.
• Review and Reinforcement:
تمرینها، کوییزها و پازلها برای تثبیت یادگیری. 🧩
نوشتن Middleware سفارشی در ASP.NET Core 🧩
ءMiddleware نرمافزاری است که در قالب یک pipeline داخل برنامه کنار هم قرار میگیرد تا درخواستها (requests) و پاسخها (responses) را مدیریت کند.
ءASP.NET Core مجموعهی غنیای از middlewareهای داخلی (built-in) را ارائه میدهد، اما در بعضی سناریوها ممکن است بخواهید یک middleware سفارشی (custom middleware) بنویسید.
کلاس Middleware
ءMiddleware معمولاً داخل یک کلاس کپسوله میشود و از طریق یک extension method در دسترس قرار میگیرد. مثال زیر یک middleware درونخطی (inline) را نشان میدهد که culture مربوط به درخواست جاری را از روی query string تنظیم میکند:
using System.Globalization;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseHttpsRedirection();
app.Use(async (context, next) =>
{
var cultureQuery = context.Request.Query["culture"];
if (!string.IsNullOrWhiteSpace(cultureQuery))
{
var culture = new CultureInfo(cultureQuery);
CultureInfo.CurrentCulture = culture;
CultureInfo.CurrentUICulture = culture;
}
// Call the next delegate/middleware in the pipeline.
await next(context);
});
app.Run(async (context) =>
{
await context.Response.WriteAsync(
$"CurrentCulture.DisplayName: {CultureInfo.CurrentCulture.DisplayName}");
});
app.Run();
ءMiddleware درونخطی بالا برای نمایش نحوهی ساخت یک middleware با استفاده از متد Microsoft.AspNetCore.Builder.UseExtensions.Use استفاده شده است. این متد، یک delegate تعریفشده بهصورت درونخطی را به pipeline درخواستهای برنامه اضافه میکند.
متدهای Use
دو overload برای متد Use وجود دارد:
یکی که HttpContext و <Func<Task میگیرد. در این حالت <Func<Task بدون پارامتر فراخوانی میشود.
دیگری که HttpContext و RequestDelegate میگیرد. در این حالت RequestDelegate با ارسال HttpContext فراخوانی میشود.
استفاده از overload دوم ترجیح داده میشود چون باعث صرفهجویی در دو allocation داخلی در هر درخواست میشود. ⚡️
تست Middleware
میتوانید middleware را با ارسال culture تست کنید. مثلاً:
https://localhost:5001/?culture=es-es
انتقال Middleware به یک کلاس
کد زیر همان middleware قبلی را داخل یک کلاس قرار میدهد:
using System.Globalization;
namespace Middleware.Example;
public class RequestCultureMiddleware
{
private readonly RequestDelegate _next;
public RequestCultureMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var cultureQuery = context.Request.Query["culture"];
if (!string.IsNullOrWhiteSpace(cultureQuery))
{
var culture = new CultureInfo(cultureQuery);
CultureInfo.CurrentCulture = culture;
CultureInfo.CurrentUICulture = culture;
}
// Call the next delegate/middleware in the pipeline.
await _next(context);
}
}
الزامات کلاس Middleware
کلاس middleware باید شامل موارد زیر باشد:
• یک constructor عمومی با پارامتر از نوع RequestDelegate
• یک متد عمومی به نام Invoke یا InvokeAsync که:
• مقدار بازگشتی آن Task باشد
• اولین پارامتر آن از نوع HttpContext باشد
پارامترهای اضافی در constructor و متد Invoke/InvokeAsync از طریق Dependency Injection (DI) مقداردهی میشوند. 💉
Extension Method برای Middleware
معمولاً یک extension method ساخته میشود تا middleware از طریق IApplicationBuilder قابل استفاده باشد:
using System.Globalization;
namespace Middleware.Example;
public class RequestCultureMiddleware
{
private readonly RequestDelegate _next;
public RequestCultureMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var cultureQuery = context.Request.Query["culture"];
if (!string.IsNullOrWhiteSpace(cultureQuery))
{
var culture = new CultureInfo(cultureQuery);
CultureInfo.CurrentCulture = culture;
CultureInfo.CurrentUICulture = culture;
}
await _next(context);
}
}
public static class RequestCultureMiddlewareExtensions
{
public static IApplicationBuilder UseRequestCulture(
this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestCultureMiddleware>();
}
}
استفاده از Middleware در Program.cs
using Middleware.Example;
using System.Globalization;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseHttpsRedirection();
app.UseRequestCulture();
app.Run(async (context) =>
{
await context.Response.WriteAsync(
$"CurrentCulture.DisplayName: {CultureInfo.CurrentCulture.DisplayName}");
});
app.Run();
وابستگیهای Middleware
ءMiddleware باید از اصل Explicit Dependencies Principle پیروی کند؛ یعنی وابستگیهای خود را در constructor مشخص کند.
🔹️ءMiddleware فقط یک بار در طول عمر برنامه ساخته میشود. 🕒
🔹️ءMiddleware میتواند وابستگیهای خود را از طریق DI در constructor دریافت کند. همچنین متد UseMiddleware میتواند پارامترهای اضافی هم بگیرد.
وابستگیهای سطح درخواست (Per-request)
از آنجا که middleware در زمان startup ساخته میشود، طول عمر آن برابر طول عمر برنامه است. بنابراین سرویسهای با Scoped lifetime که در constructor تزریق شوند، با بقیه سرویسهای هر درخواست به اشتراک گذاشته نمیشوند.
برای اشتراک سرویسهای scoped بین middleware و سایر بخشها، باید آنها را در متد InvokeAsync تزریق کنیم:
namespace Middleware.Example;
public class MyCustomMiddleware
{
private readonly RequestDelegate _next;
public MyCustomMiddleware(RequestDelegate next)
{
_next = next;
}
// IMessageWriter is injected into InvokeAsync
public async Task InvokeAsync(HttpContext httpContext, IMessageWriter svc)
{
svc.Write(DateTime.Now.Ticks.ToString());
await _next(httpContext);
}
}
public static class MyCustomMiddlewareExtensions
{
public static IApplicationBuilder UseMyCustomMiddleware(
this IApplicationBuilder builder)
{
return builder.UseMiddleware<MyCustomMiddleware>();
}
}
تست Middleware سفارشی
using Middleware.Example;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IMessageWriter, LoggingMessageWriter>();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseMyCustomMiddleware();
app.MapGet("/", () => "Hello World!");
app.Run();
تعریف Interface و Implementation
namespace Middleware.Example;
public interface IMessageWriter
{
void Write(string message);
}
public class LoggingMessageWriter : IMessageWriter
{
private readonly ILogger<LoggingMessageWriter> _logger;
public LoggingMessageWriter(ILogger<LoggingMessageWriter> logger) =>
_logger = logger;
public void Write(string message) =>
_logger.LogInformation(message);
}
جمعبندی ذهنی 🧠
ءMiddleware قلب pipeline در ASP.NET Core است.
🔹️میتواند درونخطی یا در قالب کلاس پیادهسازی شود
🔸️باید RequestDelegate و متد Invoke/InvokeAsync داشته باشد
🔹️برای وابستگیهای scoped، تزریق باید در InvokeAsync انجام شود
🔸️بهترین ابزار برای پیادهسازی cross-cutting concernها مثل:
• Logging
• Authentication
• Localization
• Exception Handling
🔖هشتگها:
#ASPNETCore #Middleware #DotNet #Backend
میخوایم یکسری مطالب درمورد یکی از معروفترین کتابهای حوزهی نرمافزار داشته باشیم 📘
مهمترین سرفصلهاش رو با هم برسی میکنیم، و هر نکتهای که به چشمم جالب و کاربردی بود رو باهاتون شیر میکنم😄
اگه دوست دارید خلاصهی مفیدِ یه کتاب قطور رو بدون دردِ کمر و سردرد ،مفهومی یاد بگیرید، این پستها احتمالاً به کارتون میان 😉
مهمترین سرفصلهاش رو با هم برسی میکنیم، و هر نکتهای که به چشمم جالب و کاربردی بود رو باهاتون شیر میکنم😄
اگه دوست دارید خلاصهی مفیدِ یه کتاب قطور رو بدون دردِ کمر و سردرد ،مفهومی یاد بگیرید، این پستها احتمالاً به کارتون میان 😉
"Domain Driven Design"
📌سرفصل : ایزوله کردن دامنه
📌سرفصل : ایزوله کردن دامنه
Telegraph
Isolating the Domain
بخشی از نرمافزار که بهطور مشخص مسائل مربوط به domain را حل میکند، معمولاً فقط بخش کوچکی از کل سیستم نرمافزاری را تشکیل میدهد، هرچند اهمیت آن بهمراتب بیشتر از اندازهٔ آن است. برای بهکارگیری بهترین تفکر خود، لازم است بتوانیم به عناصر model نگاه کنیم…
Heap و Stack Allocation در C# 🧠💾(بخش اول)
درک نحوهی تخصیص (allocation) و مدیریت حافظه در #C برای بهینهسازی عملکرد (performance) برنامه و اطمینان از استفادهی کارآمد از منابع بسیار حیاتی است.
در این راهنمای جامع، مفاهیم heap و stack allocation در #C را بررسی میکنیم؛ اینکه چگونه کار میکنند، چه زمانی استفاده میشوند، و چگونه بر عملکرد کد شما تأثیر میگذارند.
مقدمهای بر Memory Allocation
مدیریت حافظه در #C اغلب توسط runtime مربوط به NET. انجام میشود؛ به این معنا که توسعهدهندگان در بیشتر مواقع نیازی به تخصیص و آزادسازی دستی حافظه ندارند.
با این حال، درک اینکه دادههای شما چگونه و در کجا تخصیص داده میشوند — در stack یا در heap — میتواند به شما کمک کند کدی کارآمدتر و با performance بهتر بنویسید. ⚙️
در سیشارپ ، stack و heap دو ناحیهی حافظه هستند که برای ذخیرهسازی دادهها استفاده میشوند:
• ءstack برای static memory allocation استفاده میشود
• ءheap برای dynamic memory allocation استفاده میشود
ءStack Allocation چیست؟
ءstack ناحیهای از حافظه است که value typeها و pointerهای reference type را ذخیره میکند.
این ساختار از مدل Last-In, First-Out (LIFO) پیروی میکند؛ به این معنی که آخرین دادهی تخصیص دادهشده، اولین دادهای است که آزاد میشود.
به دلیل این ساختار، stack allocation بسیار سریع و کارآمد است. 🚀
ویژگیهای Stack Allocation:
🔹️Automatic Memory Management:
دادهها بهصورت خودکار هنگام خروج از scope آزاد میشوند
🔹️LIFO Structure:
استفاده از مدل LIFO باعث استفادهی بهینه از حافظه میشود
🔹️Limited Size:
اندازهی stack محدود است و برای objectهای بزرگ مناسب نیست
ءHeap Allocation چیست؟
ءheap برای dynamic memory allocation استفاده میشود.
اینجاست که reference typeها (مانند objectها، stringها و ...) تخصیص داده میشوند.
ءheap توسط Garbage Collector (GC) مدیریت میشود که بهصورت دورهای حافظهای که دیگر استفاده نمیشود را آزاد میکند. ♻️
ویژگیهای Heap Allocation:
🔹️Managed by Garbage Collector:
ء GC فرآیند آزادسازی حافظه را بهصورت خودکار مدیریت میکند
🔹️Dynamic Allocation:
ءobjectها میتوانند حتی پس از اتمام متدی که آنها را ایجاد کرده، باقی بمانند
🔹️Larger Memory Pool:
ءheap میتواند نسبت به stack objectهای بسیار بزرگتری را ذخیره کند
تفاوت بین Stack و Heap
تفاوتهای کلیدی بین stack و heap allocation در #C حول محور نحوه و زمان تخصیص و آزادسازی حافظه میچرخند.
حافظهی stack کارآمد است و برای دادههای کوتاهعمر (short-lived) که scope آنها محدود به function یا method است مناسب میباشد.
در مقابل، heap برای دادههایی استفاده میشود که نیاز دارند فراتر از scope یک method باقی بمانند؛ مانند instanceهای reference type.
ءstack مدیریت حافظهی خودکار و بسیار سریعی را فراهم میکند، اما از نظر اندازه محدود است و نمیتواند ساختارهای دادهای بزرگ و پیچیده را مدیریت کند.
ءheap در حالی که انعطافپذیری بیشتر و تخصیص حافظهی بزرگتری ارائه میدهد، نیازمند garbage collection است که مقداری overhead در performance ایجاد میکند. 📉
نحوهی عملکرد Stack در #C: مثال
برای درک بهتر stack allocation، به مثال زیر توجه کنید:
public void StackExample()
{
int a = 5;
int b = 10;
int result = a + b;
}
در مثال بالا:
اعداد صحیح a، b و result روی stack تخصیص داده میشوند زیرا از نوع value type هستند
زمانی که متد StackExample به پایان میرسد، تمام حافظهی استفادهشده توسط این متغیرها بهصورت خودکار آزاد میشود
Local Variables و Stack Allocation
متغیرهای محلی (Local variables)، شامل value typeها و reference به objectها، روی stack ذخیره میشوند.
با این حال، referenceهای مربوط به objectها به heap اشاره میکنند؛ جایی که دادهی واقعی object در آن قرار دارد.
زمانی که یک value type بخشی از یک reference type باشد (برای مثال، یک فیلد داخل یک class)، آن value type بهعنوان بخشی از object مربوط به reference type روی heap ذخیره میشود.
نحوهی عملکرد Heap در #C: مثال
کد زیر را در نظر بگیرید:
public class Person
{
public string Name;
public int Age;
}
public void HeapExample()
{
Person person = new Person();
person.Name = "Alice";
person.Age = 30;
}
در این مثال: object از نوع Person روی heap ایجاد میشود زیرا یک reference type است.reference با نام person روی stack ذخیره میشود که به object روی heap اشاره میکند.
فیلدهای Name و Age نیز بهعنوان بخشی از object مربوط به Person روی heap ذخیره میشوند
Value Typeها در مقابل Reference Typeها
درک value typeها و reference typeها به شفافسازی نحوهی استفاده از حافظه کمک میکند.
🔹️Value Types:
بهصورت مستقیم در حافظهی stack ذخیره میشوند. مانند int، float، bool و structهای سفارشی
🔹️Reference Types:
روی heap ذخیره میشوند، اما reference مربوط به آنها روی stack قرار میگیرد. مانند classها، arrayها و stringها
مثال: Value Type در مقابل Reference Type
// Value type, allocated on the stack
int x = 10;
// Reference type, reference on stack, object on heap
Person p1 = new Person();
در مثال بالا، x روی stack ذخیره میشود زیرا یک value type است، در حالی که p1 روی heap ذخیره میشود زیرا یک reference type است.
Memory Management در .NET
ءruntime مربوط به NET. شامل یک Garbage Collector (GC) است که به مدیریت تخصیص و آزادسازی حافظه برای heap کمک میکند.
ءGC بهصورت خودکار objectهایی که همچنان در حال استفاده هستند را ردیابی کرده و حافظهای که دیگر مورد نیاز نیست را آزاد میکند. ♻️
Heap و Stack Allocation در C# 🧠💾(بخش دوم)ءGC در NET. برای بهینهسازی پرفورمنس، objectها را به سه نسل تقسیم میکند:
Generational Garbage Collection
🔹️Generation 0:
ءobjectهای کوتاهعمر که معمولاً در methodها تخصیص داده میشوند
🔸️Generation 1:
ءobjectهای با طول عمر متوسط که از collection مربوط به Gen 0 عبور کردهاند
🔹️Generation 2:
ءobjectهای بلندعمر که معمولاً برای دادههای سطح application استفاده میشوند
مثال: نحوهی عملکرد GC
public void GarbageCollectionExample()
{
// Allocated on the heap (Generation 0)
Person p1 = new Person();
}
مثال Generation 0 (GarbageCollectionExample):
ءinstance مربوط به Person با نام p1 روی heap تخصیص داده شده و در Generation 0 آغاز میشود که برای objectهای کوتاهعمر در نظر گرفته شده است.
اگر پس از اتمام method هیچ reference دیگری به p1 وجود نداشته باشد، garbage collector ممکن است آن را در طی collection مربوط به Generation 0 آزاد کند.
public void Generation1Example()
{
List<int> numbers = new List<int>();
for (int i = 0; i < 1000; i++)
{
numbers.Add(i);
}
// 'numbers' will likely survive Gen 0 collection and move to Gen 1.
}
مثال Generation 1 (Generation1Example):
ء<List<int با نام numbers روی heap تخصیص داده میشود. از آنجا که شامل چندین مقدار بوده و برای مدت نسبتاً طولانیتری باقی میماند (برای مثال، تا پایان method)، ممکن است از garbage collection مربوط به Generation 0 عبور کرده و به Generation 1 ارتقاء یابد.
ءGeneration 1 برای objectهایی است که به اندازهی objectهای Generation 0 کوتاهعمر نیستند اما همچنان نیازی به باقیماندن در طول کل عمر application ندارند.
public class CacheManager
{
private static Dictionary<string, string> _cache = new Dictionary<string, string>();
public static void AddItem(string key, string value)
{
_cache[key] = value;
}
}
public void Generation2Example()
{
CacheManager.AddItem("user1", "data1");
CacheManager.AddItem("user2", "data2");
// '_cache' is a static reference and is long-lived, likely residing in Generation 2.
}
مثال Generation 2 (Generation2Example):
کلاس CacheManager از یک Dictionary بهصورت static استفاده میکند، که به این معناست دادههای آن در طول چرخهی عمر application باقی میمانند.
از آنجا که این dictionary (_cache) بلندعمر است، احتمالاً به Generation 2 ارتقاء مییابد که objectهایی را نگهداری میکند که انتظار میرود برای مدت طولانی باقی بمانند؛ مانند cacheها، دادههای static یا objectهای singleton.
مشکلات رایج و Best Practiceها
🔹️Stack Overflow:
زمانی رخ میدهد که تعداد زیادی فراخوانی method تودرتو یا recursion بیش از حد وجود داشته باشد که منجر به اتمام حافظهی stack میشود
🔸️Memory Leaks:
با اینکه NET. دارای GC است، memory leak همچنان میتواند رخ دهد اگر referenceها بهصورت ناخواسته زنده نگه داشته شوند و مانع از جمعآوری آنها توسط GC شوند
Best Practices
🔹️Minimize Long-Lived Objects:
از نگهداشتن referenceها بیش از حد لازم خودداری کنید
🔸️Use Structs Wisely:
تنها زمانی از struct استفاده کنید که type کوچک و کوتاهعمر باشد تا از تخصیص غیرضروری روی heap جلوگیری شود
🔹️Avoid Recursive Calls:
برای recursionهای عمیق، از روشهای iterative استفاده کنید تا از stack overflow جلوگیری شود
نتیجهگیری
🔹️درک تفاوتهای بین heap و stack allocation در #C میتواند به شما کمک کند applicationهایی کارآمدتر و با performance بالاتر بنویسید.
🔸️با دانستن اینکه دادههای شما کجا و چگونه ذخیره میشوند، میتوانید تصمیمات بهتری دربارهی مدیریت حافظه اتخاذ کنید، از pitfalls جلوگیری کنید و از مزایای هر دو نوع تخصیص حافظه بهره ببرید. ⚡️
🔹️برای درک کامل پتانسیل memory management، مهم است بدانید که هر نوع allocation دارای سناریوهای استفادهی بهینهی خاص خود است.
🔸️ء stack برای سناریوهایی ایدهآل است که دادهها دارای طول عمر کوتاه و مشخص هستند؛ مانند محاسبات سطح method و value typeهای ساده.
🔹️دسترسی سریع و آزادسازی خودکار آن تضمین میکند که دادههای کوچک و موقتی با حداقل overhead مدیریت شوند که منجر به performance بهینه میشود.
🔸️از سوی دیگر، heap انعطافپذیری لازم برای dynamic memory allocation و objectهای بلندعمر را فراهم میکند، اما این امر با هزینهی زمان دسترسی کندتر و نیاز به garbage collection همراه است.
🔹️برای ساختارهای دادهای پیچیده، منابع اشتراکی یا objectهایی که فراتر از method ایجادکنندهی خود باقی میمانند، heap ضروری است.
🔸️با این حال، توسعهدهندگان باید مراقب باشند referenceها را بیش از حد لازم نگه ندارند تا از memory leak و افزایش GC pressure جلوگیری شود.
🔹️ءGenerational garbage collection با دستهبندی objectها بر اساس طول عمر آنها، به ایجاد تعادل میان performance و کارایی memory management کمک میکند.
🔸️درک نحوهی ارتقاء objectها میان نسلها میتواند به توسعهدهندگان کمک کند کدی بنویسند که توقفهای GC را به حداقل رسانده و throughput را به حداکثر برساند.
🔹️در عمل، آگاهی از محل و نحوهی تخصیص حافظه میتواند به شما کمک کند کدی بنویسید که عملکرد بهتری داشته باشد، از مشکلات رایجی مانند stack overflow یا memory leak جلوگیری کند و از منابع سیستم بهصورت مؤثرتری استفاده نماید.
🔸️برای مثال، با کاهش تخصیص objectهای غیرضروری روی heap و استفاده از تخصیصهای مبتنی بر stack در صورت امکان، میتوانید overhead ناشی از garbage collection را کاهش داده و responsiveness applicationهای خود را بهبود دهید.
🔹️درک عمیق این مفاهیم حافظه همچنین شما را در انتخاب ساختارهای دادهای و design patternهای مناسب راهنمایی میکند.
🔸️برای مثال، استفاده از struct بهجای class برای objectهای کوچک و immutable میتواند به کاهش تخصیص روی heap کمک کند، در حالی که استفادهی هوشمندانه از مکانیزمهای caching میتواند تضمین کند objectهای بلندعمر در Generation 2 بهدرستی مدیریت شوند بدون آنکه استفاده از حافظه بیش از حد افزایش یابد.
🔹️در نهایت، memory management در #C ابزاری قدرتمند است اگر با رویکردی استراتژیک مورد استفاده قرار گیرد.
🔸️استفادهی مؤثر از heap و stack در کنار best practiceهای مربوط به garbage collection به توسعهدهندگان کمک میکند applicationهایی مقاوم و با performance بالا ایجاد کنند.
🔹️با افزایش پیچیدگی پروژههای شما، در نظر داشتن این مفاهیم تضمین میکند که applicationهای شما همچنان scalable، maintainable و کارآمد باقی بمانند.
🔸️به تفاوتهای میان value type و reference type توجه داشته باشید و از garbage collector مربوط به runtime در NET. بهره ببرید، در حالی که نسبت به مشکلات رایج حافظه مانند leakها و stack overflow نیز هوشیار باشید.
🔹️درک مناسب این مفاهیم در پروژههای پیچیدهتر #C به شما کمک شایانی خواهد کرد. 🚀
🔖هشتگها:
#CSharp #DotNet #MemoryManagement #GarbageCollector #Heap #Stack #Performance
📌عنوان : Factory Method
Telegraph
Factory Method
🎯Intent ءFactory Method یک creational design pattern است که یک interface برای ایجاد اشیاء در کلاس والد فراهم میکند، ولی به subclassها اجازه میدهد نوع اشیاء ایجادشده را تغییر دهند. ❗Problem تصور کنید که در حال توسعه یک logistics management application هستید.…
📌Domain Driven Design : A Model Expressed in Software
در تصور معمول ما، یک شخص دارای هویتی است که از تولد تا مرگ و حتی فراتر از آن امتداد مییابد. ویژگیهای فیزیکی آن شخص تغییر میکنند و در نهایت از بین میروند. نام ممکن است تغییر کند. روابط مالی به وجود میآیند و از بین میروند. حتی یک ویژگی هم در انسان وجود ندارد که تغییرناپذیر باشد؛ با این حال، هویت باقی میماند. آیا من همان فردی هستم که در پنجسالگی بودم؟ این نوع پرسش متافیزیکی در جستجوی مدلهای دامنه (domain models) مؤثر اهمیت دارد.
با بیانی کمی متفاوت: آیا کاربرِ اپلیکیشن اهمیتی میدهد که من همان فردی هستم که در پنجسالگی بودم؟
دو واریز (deposit) با مبلغ یکسان، به یک حساب یکسان، در یک روز یکسان، همچنان دو transaction متمایز محسوب میشوند.بنابراین دارای هویت هستند و یک ENTITY به شمار میآیند.
از سوی دیگر، attribute مبلغ این دو transaction احتمالاً instanceهایی از یک Object از نوع money هستند. این مقادیر هیچ هویتی ندارند، زیرا تمایز میان آنها هیچ سودمندیای ندارد.
در واقع، ممکن است دو Object دارای هویت یکسان باشند، بدون آنکه attributeهای یکسانی داشته باشند یا حتی الزاماً از یک class یکسان باشند.
در واقع، ممکن است یک چیز واحد در دنیای واقعی در یک domain model بهصورت یک ENTITY نمایش داده شود یا اصلاً چنین نباشد.
برای مثال، یک application جهت رزرو صندلی در یک استادیوم ممکن است صندلیها (seat) و شرکتکنندگان (attendee) را بهعنوان ENTITY در نظر بگیرد.
در حالت assigned seating که روی هر بلیت شماره صندلی درج شده است، صندلی یک ENTITY محسوب میشود. شناسه (identifier) آن شماره صندلی است که در محدودهی استادیوم یکتا (unique) است.
صندلی ممکن است attributeهای دیگری نیز داشته باشد، مانند:
• موقعیت مکانی
• داشتن یا نداشتن دیدِ مسدود
• قیمت
اما تنها شماره صندلی یا ترکیب یکتای ردیف (row) و موقعیت (position) برای شناسایی و تمایز صندلیها استفاده میشود.
از سوی دیگر، اگر رویداد از نوع general admission باشد به این معنا که دارندگان بلیت در هر صندلی خالی که پیدا کنند مینشینند نیازی به تمایز میان صندلیهای منفرد وجود ندارد.
در این حالت، تنها تعداد کل صندلیها اهمیت دارد. با اینکه شماره صندلیها همچنان بهصورت فیزیکی روی صندلیها حک شدهاند، هیچ نیازی نیست که software آنها را ردیابی کند.
در واقع، اگر مدل شماره صندلی خاصی را به بلیتها نسبت دهد، این یک خطای مدلسازی خواهد بود؛ زیرا در یک رویداد با general admission چنین محدودیتی وجود ندارد.
در چنین شرایطی، صندلیها دیگر ENTITY نیستند و هیچ identifierای نیز موردنیاز نخواهد بود.
🔖هشتگها :
#DDD #DomainModel
#Entity
Forwarded from tech-afternoon (Amin Mesbahi)
🪞روز مهندس، و داستان جناب هانسکریستیناندرسون که هنوز تمام نشده!
قرن ۱۹ میلادی، نویسنده سرشناس دانمارکی، در کنار داستانهای به ظاهر سادهای مثل دختر کبریتفروش یا جوجهاردک زشت، داستان لباس جدید پادشاه رو نوشت که احتمالا اکثر ما یا کتابش رو در کودکی خوندیم، یا کارتونش رو تماشا کردیم.
فکر میکنم بد نباشه جامعه مهندسی نرمافزار، گاهی جلو آیینه بایسته و ببینه که چقدر لُخت است! توی قصه «پادشاه لخت»، مشکل فقط یک پادشاه سادهلوح نبود. مسئله، یک اکوسیستم کامل بود:
- خیاطهای دروغین
- درباریان تأییدکننده
- جمعیتی که جرئت پرسیدن نداشت
و فقط یک کودک بود که گفت: چیزی تنش نیست!
اگر بخوایم صادق باشیم، ما هم در اکوسیستم نرمافزار، کم از اون دربار نداریم. و چقدر برای مناسب ظاهر شدن، نیاز به یادگیری و تلاش مضاعف داریم. خیاطهای دروغین امروز همیشه شیادهای بیرونی نیستن.
گاهی در لباس لیدر فنی ظاهر میشن که بدون threat model و بدون طراحی امنیتی، سیستم رو «production-ready» اعلام میکنن.
گاهی در قامت تیم محصول که زمان تحویل را بالاتر از امنیت و کیفیت مینشونن.
گاهی در نقش مدیر که بودجه آموزش، معماری، یا امنیت رو هزینه اضافی میدونن.
و گاهی هم در قامت مهندس باتجربهای که سالهاست چیز جدیدی یاد نگرفته ولی همچنان با اعتمادبهنفس حرف میزنه.
و درباریان؟
ما وقتی بدون خوندن دقیق PR رو تأیید میکنیم.
وقتی postmortem واقعی نمینویسیم.
وقتی ضعف امنیتی رو میدونیم ولی میگیم “بعداً درستش میکنیم”.
وقتی outage رو «اتفاق طبیعی» جا میزنیم.
مسئله این نیست که هک شدیم یا نشدیم. کند یا ناپایدار هستیم یا نه!
مسئله اینه که آیا اصول مهندسی رو جدی گرفتهایم یا نه.
اما سؤال جدی اینه:
چند تیم واقعاً اینها رو در عمل اجرا میکنند، نه فقط در رزومه؟
اگر بخواهیم جلوی آینه بایستیم، شاید بهتر باشد به جای شعار، این چکلیست رو از خودمون بپرسیم:
آیا ما اینها رو داریم؟
- اصول Security by Design، نه Security after Incident
- مفاهیم Threat Modeling مستند
- ساختار Secure SDLC واقعی، نه اسلاید پاورپوینت
- مکانیزمهای observability جدی
- ساختار Data Governance مشخص
- طبقهبندی داده و سیاست retention
- مدیریت دسترسی مبتنی بر اصل Least Privilege یا زیروتراست
- اصول Software Composition Analysis و مدیریت dependency
- ساختار Incident response plan تمرینشده
و...
اگر اینها نیست، ما فقط امیدواریم، مهندسی نمیکنیم.
روز مهندس شاید بیشتر از اونکه تبریک بخواد، احتیاج به صداقت داره.
صداقت با خودمون.
شاید وقتش باشه به جای تبریکهای شاعرانه، هرکدوممون یک ضعف مهندسی رو در تیممون جدی اصلاح کنیم. (جلو آینه بایستیم و لخت بودنمون رو ببینیم!)
لباس، با آرزو دوخته نمیشه.
با استاندارد، تمرین و مسئولیتپذیری دوخته میشه.
قرن ۱۹ میلادی، نویسنده سرشناس دانمارکی، در کنار داستانهای به ظاهر سادهای مثل دختر کبریتفروش یا جوجهاردک زشت، داستان لباس جدید پادشاه رو نوشت که احتمالا اکثر ما یا کتابش رو در کودکی خوندیم، یا کارتونش رو تماشا کردیم.
فکر میکنم بد نباشه جامعه مهندسی نرمافزار، گاهی جلو آیینه بایسته و ببینه که چقدر لُخت است! توی قصه «پادشاه لخت»، مشکل فقط یک پادشاه سادهلوح نبود. مسئله، یک اکوسیستم کامل بود:
- خیاطهای دروغین
- درباریان تأییدکننده
- جمعیتی که جرئت پرسیدن نداشت
و فقط یک کودک بود که گفت: چیزی تنش نیست!
اگر بخوایم صادق باشیم، ما هم در اکوسیستم نرمافزار، کم از اون دربار نداریم. و چقدر برای مناسب ظاهر شدن، نیاز به یادگیری و تلاش مضاعف داریم. خیاطهای دروغین امروز همیشه شیادهای بیرونی نیستن.
گاهی در لباس لیدر فنی ظاهر میشن که بدون threat model و بدون طراحی امنیتی، سیستم رو «production-ready» اعلام میکنن.
گاهی در قامت تیم محصول که زمان تحویل را بالاتر از امنیت و کیفیت مینشونن.
گاهی در نقش مدیر که بودجه آموزش، معماری، یا امنیت رو هزینه اضافی میدونن.
و گاهی هم در قامت مهندس باتجربهای که سالهاست چیز جدیدی یاد نگرفته ولی همچنان با اعتمادبهنفس حرف میزنه.
و درباریان؟
ما وقتی بدون خوندن دقیق PR رو تأیید میکنیم.
وقتی postmortem واقعی نمینویسیم.
وقتی ضعف امنیتی رو میدونیم ولی میگیم “بعداً درستش میکنیم”.
وقتی outage رو «اتفاق طبیعی» جا میزنیم.
مسئله این نیست که هک شدیم یا نشدیم. کند یا ناپایدار هستیم یا نه!
مسئله اینه که آیا اصول مهندسی رو جدی گرفتهایم یا نه.
من قبلاً دو اپیزود درباره تولید امن نرمافزار ساختم. درباره: SSDLC, STRIDE, Shift-left, SAST, DAST, IAST, RASP, SCA
در مورد بدهی فنی هم یه ویدیو کوتاه
اما سؤال جدی اینه:
چند تیم واقعاً اینها رو در عمل اجرا میکنند، نه فقط در رزومه؟
اگر بخواهیم جلوی آینه بایستیم، شاید بهتر باشد به جای شعار، این چکلیست رو از خودمون بپرسیم:
آیا ما اینها رو داریم؟
- اصول Security by Design، نه Security after Incident
- مفاهیم Threat Modeling مستند
- ساختار Secure SDLC واقعی، نه اسلاید پاورپوینت
- مکانیزمهای observability جدی
- ساختار Data Governance مشخص
- طبقهبندی داده و سیاست retention
- مدیریت دسترسی مبتنی بر اصل Least Privilege یا زیروتراست
- اصول Software Composition Analysis و مدیریت dependency
- ساختار Incident response plan تمرینشده
و...
اگر اینها نیست، ما فقط امیدواریم، مهندسی نمیکنیم.
روز مهندس شاید بیشتر از اونکه تبریک بخواد، احتیاج به صداقت داره.
صداقت با خودمون.
شاید وقتش باشه به جای تبریکهای شاعرانه، هرکدوممون یک ضعف مهندسی رو در تیممون جدی اصلاح کنیم. (جلو آینه بایستیم و لخت بودنمون رو ببینیم!)
لباس، با آرزو دوخته نمیشه.
با استاندارد، تمرین و مسئولیتپذیری دوخته میشه.
منظورم از اکوسیستم، فقط گروه خاصی نیست که ربطش بدیم به وقایع سیاسی و اجتماعی کنونی و بگیم اونا که از ما نیستن یا رفتنیاند و بعدش درستش میشه و از خودمون صلب مسئولیت کنیم.
از استارتاپهای متعدد خصوصی که هک شدن، تا بانک و سازمانی که با هک شدن دادههاش نابود شد و از روی پیامکها موجودی مردم رو بازسازی! کردن! تا نرمافزار دانشگاه تا... همه و همه گواهی بر لُختی جامعه نرم افزاریه!