21 Şubat 2023 Salı

Devops Terms

CI CD Pipelie

Pipeline kodumuzu otomatik olarak build eder, test eder ve her hangi bir clouda deploy eder.


Docker İmages

Her Image bir Base Image'dan türemektedir. Tüm Base Image'lar içerisinde Linux'un çok küçük bir Core'unu bulundurur. Yani kısaca her Image bir işletim sistemi barındırır ve biz container ayağa kaldırdığımızda o container içerisinde çalışan Image bir işletim sistemi üzerinde çalışır. Dolayısı ile o işletim sistemine bağlanabiliriz ve hatta kendi komutlarımızı onun üzerinde çalıştırabiliriz.


Kubernetes

Yazmış olduğumuz kodu dockerize edip bir image içerisine koyuyoruz. Bu imageden bir adet container oluşturup bunu kubrnetes pod içerisine koyuyoruz. Pod içerisinde secrets, maps yada volumes mount ediyoruz. Podun önüne kubernetes servis koyuyoruz bu servise de load balancer kullanan ingress aracılığıyla erişiyoruz.

Aggregates

  •  Aggregateler, Entity ve Value Objectlerin kümesidir.
  • Entity ve Value Objectlerin tek bir transaction içerisinde yönetilmesini sağlar. Dolayısıyla Entity ve Value Objectlerin tutarlılığından sorumludur. 
  • Aggregateler bir Id ye sahiptir ve aynı zamanda bir Entitydir.
  • Bir aggregate, başka bir aggregateyi navigation property olarak içermemelidir. Sadece Id sini içermelidir.
𝗛𝗼𝘄 𝘁𝗼 𝗗𝗲𝘁𝗲𝗿𝗺𝗶𝗻𝗲 𝗶𝗳 𝗮𝗻 𝗢𝗯𝗷𝗲𝗰𝘁 𝗦𝗵𝗼𝘂𝗹𝗱 𝗕𝗲 𝗧𝗿𝗲𝗮𝘁𝗲𝗱 𝗮𝘀 𝗮𝗻 𝗔𝗴𝗴𝗿𝗲𝗴𝗮𝘁𝗲 𝗥𝗼𝗼𝘁?🌟

In Domain-Driven Design, an Aggregate Root is a fundamental concept representing the entry point to an aggregate.

a cluster of related objects treated as a single unit for data management and consistency.

I often use a simple question to determine the Aggregate Root: 𝗦𝗵𝗼𝘂𝗹𝗱 𝗱𝗲𝗹𝗲𝘁𝗶𝗻𝗴 𝘁𝗵𝗲 𝗔𝗴𝗴𝗿𝗲𝗴𝗮𝘁𝗲 𝗥𝗼𝗼𝘁 𝗮𝗹𝘀𝗼 𝗿𝗲𝗾𝘂𝗶𝗿𝗲 𝗱𝗲𝗹𝗲𝘁𝗶𝗻𝗴 𝘁𝗵𝗲 𝗼𝗯𝗷𝗲𝗰𝘁𝘀 𝗶𝗻 𝘁𝗵𝗲 𝗮𝗴𝗴𝗿𝗲𝗴𝗮𝘁𝗲?

Consider an e-commerce system with entities like Order, OrderItem, and Customer. The Order serves as the Aggregate Root because deleting it should also delete OrderItems, which are meaningful only within the context of an Order. On the other hand, the Customer entity remains valid even after the deletion of the Order, indicating that it's not a good candidate to be inside the aggregate root.


İmplementasyon

  • Domain layera MediatR linki üzerindeki nuget paketi indirilir.

  • Doman > Primitives  altına  Entity.cs ,IDomainEvent.cs ve AggregateRoot.cs  isminde üç adet class oluşturulur.

  • Entity.cs
  public abstract class Entity : IEquatable<Entity>
    {
        protected  Entity(Guid id)
        {
            Id = id;
        }
        protected Entity()
        {

        }
        public Guid Id { get; private init; }

        public override bool Equals(object? obj)
        {
            if(obj is null)
            {
                return false;
            }

            if(obj.GetType() != GetType())
            {
                return false;
            }

            if(obj is not Entity entity)
            {
                return false;
            }
            return entity.Id == Id;
        }

        public bool Equals(Entity? other)
        {
            if (other is null)
            {
                return false;
            }

            if (other.GetType() != GetType())
            {
                return false;
            }

            return other.Id == Id;
        }
        public override int GetHashCode()
        {
            return Id.GetHashCode();
        }

        public static bool operator ==(Entity? first, Entity? second)
        {
            return first is not null && second is not null && first.Equals(second); 
        }
        public static bool operator !=(Entity? first, Entity? second)
        {
            return !(first == second);
        }
    }
  • IDomainEven.cs
    public interface IDomainEvent : INotification
    {
    }
  • AggregateRoot.cs
    public abstract class AggregateRoot : Entity
    {
        private readonly List<IDomainEvent> _domainEvents = new();
        public AggregateRoot(Guid id) 
            : base (id)    
        {

        }
        protected AggregateRoot() { }

        public IReadOnlyCollection<IDomainEvent> GetDomainEvents() => _domainEvents.ToList();
        public void ClearDomainEvents() => _domainEvents.Clear();
        protected void RaiseDomainEvent(IDomainEvent domainEvent)
        {
            _domainEvents.Add(domainEvent);
        }
    }

17 Şubat 2023 Cuma

Portainer

docker-compose.yml isminde bir file oluşturup aşağıdaki komutları içine yapıştırıyoruz. Daha sonra cmd consoldan " docker-compose up  " diyerek portaineri ayağa kaldırıyoruz.

version: '3.8'
services:
  portainer:
    image: portainer/portainer-ce
    restart: always
    container_name: portainer
    ports:
      - "8000:8000"
      - "9000:9000"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_data:/data
volumes:
  portainer_data:


reference

https://medium.com/devopsturkiye/docker-ile-portainer-kurulumu-ve-portainera-h%C4%B1zl%C4%B1-bak%C4%B1%C5%9F-2fdcf2b31deb


Jenkins

Jenkins bir CI(continious integration) aracıdır. Continous Entegrationun  en büyük amacı hataları hızlı bir şekilde ortaya çıkarmasıdır.  Jenkins Projelerin deploy, build ve test işlemlerinin otomatik bir şekilde gerçekleşmesini sağlar. Bunları belli aralıklarla yapıp ve duruma göre bize mail, notification veya feedback verecek şekilde ayarlamalar yapabiliyoruz.


Jenkins nasıl çalışır ? 

Developer git aracılığıyla kodunu  repositoye( Github, Gitlab vs.) commitler. Jenkins sürekli olarak repositoyi dinlemektedir. Kod commit edildiği an bunu yakalar, kodu repositoryden alır ve Build'i tetikler.  Build başladığında, bittiğinde veya bir sorun olduğunda size notification gönderiyor olacaktır. Şayet build kısmınız başarılı olduysa test kısmına geçirir. Test kısmında da hata bulunamazsa deploy edilir.



Pipeline

İşlerin ardışık bir sırada yapılması, bir işlemin çıktısının sonraki gelen işlemin girdisi olması anlamına gelmektedir. Bu yüzden bir sonraki evreye geçebilmek için önceki evrenin başarılı bir şekilde tamamlanmış olması gerekiyor. Adımlar arasında geri bildirimler de vardır.



Stage

Stage , pipeline içerisinde yer alan fazları ifade eder. Mesela aşağıdaki fotoğrafta gördüğünüz Build bir stage’dir. Step ise stage içerisinde yer alan adımları ifade eder. Stage ve step’leri oluştururken pipeline script’i kullanıyoruz.



Docker Jenkins Kurulumu

docker run -p 8080:8080 -p 50000:50000 -v /var/tmp/jenkins_home:/var/jenkins_home -u root jenkins/jenkins

http://localhost:8080/ portuna gidersek jenkinsin ayağa kalktığını göreceğiz. Ayağa kalktığında aşağıdaki ekranı görmüş olacağız. 

Jenkins ilk ayağa kalktığında bize bir şifre vermektedir. Bu şifreyi terminalden alıp buraya yukarıdaki Administrator password alanına yapıştırıyoruz.


refrenece



13 Şubat 2023 Pazartesi

Expression<𝐅𝐮𝐧𝐜<>>

Func ve Expression

Func değer döndüren bir metot'tur. Kısacası Func sadece bir değer döndüren metodu bir değişkende tutmamızı sağlayan bir tiptir.


IEnumerable<T> Func<T> kabul ederken IQueryable<T> Expression<Func<T>> kabul etmektedir.

Func<T> vs. Expression<Func<T>>

  • The IEnumerable version: Where(Func<T, bool> predicate)
  • The IQueryable version: Where(Expression<Func<T, bool>> predicate)

Kullanım Şekilleri ikisinde de aynıdır.
IEnumberable version: .Where(x => x.property == "value")
IQueryable version: .Where(x => x.property == "value")




Lazy/Eager/Explicit Loading

Entityler içerisinde bulunan ilişkili verilerin yüklenmesini yönetmek için Lazy, Eager ve Explicit olmak üzere 3 yöntem vardır.

Entity framework’ün ilişkili davranışı yüklemedeki default davranışı eager loadingdir. Bu yüzden entity framework üzerinde lazy loading yapmak istiyorsak, nuget üzerinden EntityFrameworkCore.Proxies paketini yüklemeliyiz.


User ve Order tablosu arasına user 1 .......n Order ilişkisi olduğunu düşünelim.



Lazy Loading

Lazy loading uygulamak için EntityFrameworkCore.Proxies nuget paketini indiriyoruz.Ve startup.cs üzerinde aşağıdaki configurasyonu yapıyoruz.


Entity framework üzerinde yükleme türü olarak lazy loading kullanıldığında, ilişkili veriler yüklenmez. Entity sınıfı üzerinden ne zaman ilişkili bir alana erişilmek istenirse, o zaman veri tabanına ilişili tabloya özel yeni bir sorgu atılarak ilişkili veriler getirilir.


  • Yukarıda ilk satır için sadece users getirilmiştir. Daha sonra user.Orders querysi için veritabanına yeni bir sorgu atılmıştır ve Orders getirilmiştir.
  • lazy loading kullanılan işlemlerde, her bir ilişkili alana erişim isteğinde veri tabanına yeni bir query gönderilecektir. Fazla kayıt olan işlemlerde bu performans sorunu doğurabilir. İkinci sorguyu ilişkili tabloya atılıyor buna dikkat edelim yani orders tablosuna.




Eager Loading

Eager loading, veri tabanından kayıtlar çekilirken getirilmesi istenen ilişkili alanların da beraberinde getirilme işlemidir. Eager loading kullanarak ilişkili alanların tek sorguda getirilmesini sağlayabiliyoruz, ancak sonrasında getirilmemiş ilişkili alanlara erişme isteğinde veriler getirilmeyecektir.

Getirilmesi istediğimiz alanları “Include” methodu ile belirtebiliyoruz.




Explicit Loading

Explicit loading için, aslında lazy ve eager loadingin karışımı diyebiliriz. Veri tabanından veriler çekilirken, hiç bir ilişkili alan getirilmez. Eager loadingden farkı, ilişkili alanları sonradan yükleyebilmemizdir.

İlk satırı çalıştırdığımızda, belirtildiği gibi sadece user tablosuna sorgu atılmakta. İkinci satırı çalıştırdığımızda, bu user nesnesi için ilişkili siparişleri entity üzerinde yüklemekte.

Aynı lazy loadingde olduğu gibi order tablosuna ayrı bir sorgu oluşturuldu ve ilgili siparişler getirildi. Buradaki kilit nokta, ilişkili dataların yüklenmesi komutunu bizim kendimizin vermiş olması.




REFERENCE

SQL JOIN

 


Global Query Filter

Aşağıda users tablosunda bulunan active kolonunun değerine userların aktif olup olmadığı belirleniyor. Her sorgu da bu kolonu kontol etmek yerine global query filter kullanabiliriz.


Aşağıdaki gibi configurasyon yaparak global query filter kullanabiliriz. Artık querylr içerisinde acvtive user filterini yazmamıza gerek kalmayacaktır.



Eğer ki bu global query filteri ignore ederek sorular yazmamız gerekirse;






CI/CD (Continuous Integration/Continuous Delivery)



CI : Sürekli Entegrasyon

CD: Sürekli Teslimat, Sürekli Dağıtım

CI/CD’yi, yeni kodu entegre ederken geliştirme ve operasyon ekiplerinin yaşayacağı sorunlara çözüm olabilmek adına uygulanan bir süreç olarak tanımlayabiliriz.

CI/CD, bir uygulamanın yaşam döngüsü boyunca, devam eden bir otomasyonu ve sürekli izlemeyi tarifler. (entegrasyon, test, teslimat ve dağıtım aşamaları)

CI(continuous Integration), Developerlar tarafından uygulamaya eklenen her yeni kod değişikliklerinin, düzenli olarak, build edilmesi, test edilmesi ve paylaşımlı bir repositoryde (Gitlabi Github vs.) tutulması gerekir. Bir uygulamanın aynı anda geliştirme aşamasında olan birbiriyle çelişebilecek bileşenlerinin oluşturabileceği sorunlara karşı bir çözüm olarak geliştirilmiştir.

CD(continuous delivery), geliştiricinin uygulamaya eklediği her değişikliğin otomatik olarak hata testlerinin yapılması ve GitHub gibi bir havuza yüklenmesini ifade eder. Operasyon ekibi de, bu havuzdan kodun üretim ortamına dağıtımını yapar.





CI’de “build, test ve merge” işlemleri var, 

CD’de havuza otomatik sürüm çıkıyoruz fakat deploy etmiyoruz, ikinci CD’de ise üretim ortamına otomatik dağıtım yapıyoruz.

Modern uygulamalar için, aynı uygulamanın farklı özelliklerini aynı anda yazan farklı geliştiriciler vardır. Nihayi hedef, tüm bu farklı özellikler bir araya getirildiğinde sağlıklı çalışan bir uygulamaya sahip olabilmektir. Diğer taraftan, eğer yalıtılmış olarak bir geliştirici çalışıyorsa, uygulamada yaptığı bir değişiklik, diğer geliştiricilerin  yaptığı değişikliklerle çelişebilir, çakışabilir.

CI’da uygulamayı build etmeyi otomatikleştirdik, unit ve integration testlerini otomotize bir şekilde yaptık, CD’de, doğrulanmış kodumuzu ortak havuzumuzda artık otomatik olarak sürümlüyoruz/yayınlıyoruz. Dolayısıyla, etkili bir CD için, uygulama yaşam döngüsü için zaten planlanmış bir CI’ın yerleşik bir şekilde çalışır olmasını sağlamamız gerekir. CD yani sürekli teslimin amacı, bir anlamda, üretim ortamına dağıtmak için her daim hazır olan bir kod tabanına sahip olmaktır diyebiliriz. Sürekli teslimde, kod değişikliklerinin birleştirilmesinden “üretime hazır yapıların” teslimine kadar her aşama, test ve kod yayınlama otomasyonunu içerir. Bu sürecin sonunda, operasyon ekibi, uygulamayı üretime hızlı ve kolay bir şekilde dağıtabilir.

İyi tasarlanmış bir CI/CD ardışık sürecinin son aşaması sürekli dağıtımdır, ikinci CD. Sürekli teslimat, üretime hazır bir yapının bir kod deposuna yayınlanmasını otomatikleştirirken, sürekli dağıtım, bir uygulamanın üretim ortamına yayınlanmasını/dağıtılmasını otomatikleştirir. Sürekli dağıtım, iyi tasarlanmış test otomasyonunu gerektirir


reference

https://www.linkedin.com/feed/update/urn:li:activity:6860660386396422144/

10 Şubat 2023 Cuma

.Net Core Application and SQL Server with Docker and Docker Compose

 Cmd açılıp aşağıdaki kod çalıştırılır. Böylece sql server  dockerda kurulmus olur.

docker run -it 
    -e "ACCEPT_EULA=Y" 
    -e "SA_PASSWORD=A&VeryComplex123Password" 
    -p 1433:1433 
    --name sql-server-2022 
    mcr.microsoft.com/mssql/server:2022-latest
  • -e "ACCEPT_EULA=Y", sets an environment variable read by SQL Server stating that you accept the end user license agreement
  • -e "SA_PASSWORD=A&VeryComplex123Password", sets an environment variable for the system administrator password
  • -p 1433:1433, forwards the SQL Server port from inside the container to outside the container, without this you could not access SQL Server from your host computer
  • --name sql-server-2022, give the container instance a name
  • mcr.microsoft.com/mssql/server:2022-latest, the image to download from Docker hub

Docker Compose 

Projenin ana dizininde Dockerfile ve docker-compose.yml isminde iki farklı dosya oluşturulur ve bunların içerisi aşağıdaki gibi configura edilir. Swagger .net core projesinde default olarak development ortamda çalışır. Onun için ENV olarak dockerfile içerisinde Development eklendi.

DockerFile
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
ENV ASPNETCORE_ENVIRONMENT=Development 
COPY ["WebApi/WebApi.csproj", "WebApi/"]
COPY ["Application/Application.csproj", "Application/"]
COPY ["Domain/Domain.csproj", "Domain/"]
COPY ["Infrastructure/Infrastructure.csproj", "Infrastructure/"]
RUN dotnet restore "WebApi/WebApi.csproj"
COPY . .
WORKDIR "/src/WebApi"
RUN dotnet build "WebApi.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "WebApi.csproj" -c Release -o /app/publish /p:UseAppHost=false

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "WebApi.dll"]

docker-compose.yml

version: "3.9"  # optional since v1.27.0
services:
  web_api:
    build: . # build the Docker image 
    container_name: web_api_application
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
    ports:
      - "5000:80"
  sql:
    image: "mcr.microsoft.com/mssql/server:2022-latest"
    container_name: sql_server2022
    ports: # not actually needed, because the two services are on the same network.
      - "1433:1433" 
    environment:
      - ACCEPT_EULA=y
      - SA_PASSWORD=A&VeryComplex123Password


Appsetting.js
  "ConnectionStrings": {
    "Default": "Server=sql_server2022;Database=SalesDb;User Id=SA;Password=A&VeryComplex123Password;MultipleActiveResultSets=true;TrustServerCertificate=True;Encrypt=false"
  }


Uygulama ana dizini cmd ile açılır ve "docker-compose up" komutuyla ayağa kaldırılır.





PUSH IMAGES TO DOCKER HUB

Projede Dockerfile dizininr gelip aşağıdaki kodalrı çalıştıyoruz. İkinci satırda 8ad ile başlayan yeri kendi image Idmiz ile değiştiriyoruz.
docker build --rm -f "Dockerfile" -t efcleanarchitecturetemplate:latest .

docker tag 8ad130b70635 aytac3737/efcleanarchitecturetemplate:latest
docker push aytac3737/efcleanarchitecturetemplate


Gİthub Örnek Proje






















Data Seeding

 Data Seeding yapaiblmek için 2 farklı yöntem kullanabilirz.

1- Automatic Initialisation

 İnfrastructure Layer altında DbInitializer isminde bir sınıf oluşturuyor.


Program.cs altında 


2- Model Seed Data





9 Şubat 2023 Perşembe

Record Type

  • İmmutable, recordların propertyleri create ediltikten sonra artık değiştirilemez.
  • Value-Based Equality, aynı record türünün iki instancesi, propertyleri için aynı valuelara sahip olmaları durumunda eşit olduğu anlamına gelir.

init’ keyword’ü ile işaretlenen propertyler  nesne üretildikte sonra artık  değiştirilemezler. Benzer şekilde  bir objeyi bütünsel olarak değişmez yapmak istiyorsak burada Recordlar devreye girmektedir. Böylece recordlar sayesinde immutable obje oluşturabiliriz. 

Record, bir objenin komple sabit/değişmez olarak kalmasını sağlamakta ve bu durumu güvence altına almaktadır. Böylece bu obje, artık değeri değişmeyeceğinden dolayı esasında objeden ziyade bir değer gözüyle bakılan bir yapıya dönüşmektedir.

Class’lar da verisel olarak nesne ön plandadır ve her bir farklı referansa sahip olan nesne farklı değer olarak algılanmaktadır.
Dolayısıyla Equals(x, y) karşılaştırması yanlıştır.

Recordlar da ise farklı objelerde de olsa, veriler(property değerleri) aynı olduğu sürece Equals(x, y) önermesi doğru olacaktır.

Record türünde yaratılacak nesneler aralarında karşılaştırıldıklarında referans olarak değil, verisel olarak değerlendirilir ve veriler eşitse bu nesneler eşit kabul edilir.


UserOne ve UserTwo record type oluşturma şekilleride ikiside aynı şey demektir. İkisi içinde  Id ve Name değerleri obje oluşturulacağı zaman belirlenir ve bir daha bu değerler değiştirilemez.

INHERİTANCE



Equals


Recordlarda equals valuelara göre kontrol edilir. İki farklı recordun valueları eşitse bu recordlar eşit kabul edilir.

with  Expression

With keywordu ile bir record instancesi referans alarak sadece değişmesini istediğimiz propery ismi with içerisinde belirtilir ve yeni instance oluşturulur. Aşağıda userOne nesnesin Id değeri aynı kalacak fakat name değeri Veli olacak şekilde yeni bir nesne üretmiş oluyoruz.

Positional Record 


Yukarıda görüldüğü üzere direkt olarak record tanımlamasının yanında parantezler ile constructor misali obje içerisindeki ilgili property’ler tanımlanmaktadır. Bu property’ler default ‘init’ olacak şekilde oluşturulmakta ve nesne üretimi esnasında constructor’dan verilecek değerlerden sonra readonly olarak kullanılmaktadır ve artık değiştirilememektedir. Fakat Age propertysi init olmadığı için  değiştirilebilir durumdadır.


CLASS vs RECORD

  • Classların iki instances/objects karşılaştırmak için bellek adrreslerine bakmamız gerekirken recordlar için  property values karşılaştırılması yeterlidir. Recordlar için iki nesnenin propertyleri eşitse bu recordlar eşit kabul edilir
  • Record, data/properties depolamak için tasarlanırken, Classlar logic/operations/behavior için tasarlanır.
  • DTO'lar gibi sadece data taşıyan logic içermeyen yapılar için record kullanabiliriz. 

DTO vs RECORD

Data Transfer Object(DTO'lar) ve Recordlar , bir uygulamanın farklı katmanları arasında data transfer etmek  için kullanılan iki önemli kavramdır. Hem DTO'lar hem de Recordlar, dataları yönetmeyi kolaylaştırmak için tasarlanmıştır.

DTO'lar, bir uygulamanın Presentation Layerı ile Persistance Layerı gibi farklı bölümleri arasında data transfer etmek  için kullanılan basit veri yapılarıdır. DTO'lar genellikle dataların bir network üzerinden  iletilebilecek bir forma kolayca dönüştürülmesine izin veren  serialization ve deserialization ile birlikte kullanılır. Recordlar immutable oldukları için layer arasında aktarılırken yanlışlıkla değiştirilmesin diye DTO'lar yerine kullanılabilirler. 

  • DTO'lar ve Recordlar arasında seçim yaparken, uygulamanızın özel gereksinimlerini göz önünde bulundurmanız önemlidir. Transfer edilecek objenin immutable olmasını istiyorsak recordları tercih etmeliyiz. Ancak, uygulamanız eski C# sürümleriyle uyumluluk gerektiriyorsa veya transfer edildikten  sonra dataları değiştirebilmeniz gerekiyorsa, DTO'ları tercih edebiliriz.
REFERENCE

 C#’ta Class, Record ve Struct Arasındaki Farklar
C# dilinde nesneleri temsil etmek için class, record ve struct gibi farklı türler bulunur.

Peki, bunların farkları nelerdir?
· Class (Referans Tipi)
Bellekte heap üzerinde tutulur.
Referans tipi olduğu için nesne kopyalandığında sadece referansı aktarılır.
Değiştirilebilir (mutable) yapılar için uygundur.

· Record (Referans veya Değer Tipi)
Varsayılan olarak immutable (değiştirilemez) yapıdır.
Value-based equality kullanır (içerik karşılaştırması yapar).
class veya struct olarak tanımlanabilir.

· Struct (Değer Tipi)
Stack üzerinde tutulur (performans açısından avantajlıdır).
Kopyalama yapıldığında yeni bir nesne oluşturulur.
Küçük ve sık kullanılan veri yapıları için uygundur.

 Özet:
Referans tipi istiyorsanız: Class veya Record (class)
Değer tipi ve immutable yapı istiyorsanız: Record (struct)
Değer tipi ve mutable yapı istiyorsanız: Struct