Node.js Clean Architecture ile REST API(Express) – 01

Estimated reading time: 19 minute(s)

Merhaba, bu yazımda Node.js kullanarak Clean Architecture mimarisi üzerine Express framework ile REST API geliştireceğiz.

Clean Architecture nedir konusuna burada yer vermek istemiyorum, bu yazıda daha çok uygulamalı olarak göstermek istiyorum. Google’da kısa bir araştırma sonrasında bolca Türkçe, İngilizce kaynak bulabilirsiniz.

Clean Architecture kısaca; birbirinden ayrı katmanlar, birbirine bağımlı olmayan modüller, rahatça geliştirilebilir ve test edilebilir bir yapı olarak özetlenebilir.

Yukarıda Clean Architecture yapısına baktığımızda; Entities katmanının başka hiçbir katmanla işinin olmadığını ve Use Case’lerimizin dışarıdan herhangi bir katmana bağımlılığı olmadığını görüyoruz. Konunun temeli tam olarak burada yatıyor.

01. Node.js ile Proje Oluşturma

Başta bilgisayarınızda Nodejs yüklü olması gerekiyor, aşağıdaki linkten indirebilirsiniz.
https://nodejs.org/en/download/

Biz projemizde paket yönetimi için NPM kullanacağız.

Yeni bir proje klasörü oluşturuyoruz ve içerisinde npm init komutunu çalıştırıp, sonrasında gelen sorulara proje bilgilerimizi dolduruyoruz.

npm init
package name: (ls) nodejs-clean-architecture-rest-api
version: (1.0.0)
description: Nodejs Clean Architecture ile Rest Api
git repository:
keywords:
author: Mehmet Yıldırım
license: (ISC)

Is this OK? (yes) yes

02. Uygulamamız Ne Yapacak?

Örnek uygulamamızda bir Person olacak ve aşağıdaki case’leri karşılayacak bir RESTful api geliştireceğiz.

  1. GetPerson
  2. GetAllPersons
  3. CreatePerson
  4. UpdatePerson
  5. DeletePerson
  6. LoginPerson

03. Core Katmanının Oluşturulması

Temelde Core katmanı olacak. Bunun için aşağıdaki gibi proje dizininde klasörleri oluşturalım.

  • src
    • core
      • entities
      • usecases
      • contracts
      • dtos
        • requests

Entities

entities/Person.js

'use strict';

module.exports = class {
    constructor(id = null, firstName, lastName, email, password) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
        this.password = password;
    }
};

Contracts

Person’ımızın yapabileceklerini belirlediğimiz bir sözleşme ekliyoruz.

contracts/PersonRepository.js

'use strict';

module.exports = class {

    get(personId) {
        throw new Error('not implemented');
    }

    list() {
        throw new Error('not implemented');
    }

    create(request) {
        throw new Error('not implemented');
    }

    update(personId, request) {
        throw new Error('not implemented');
    }

    delete(personId) {
        throw new Error('not implemented');
    }

    login(request) {
        throw new Error('not implemented');
    }
}

Person; get, list, create, update, delete ve login işlemlerini yapabilecek.

Usecases

Şimdi sıra geldi usecase’lerimizi yazmaya.

usecases/GetAllPerson.js

'use strict';

const PersonDto = require("../dtos/PersonDto");

module.exports = async (personRepository) => {

    var persons = await personRepository.list();
    if (persons != null)
        return persons.map((person) => {
            return new PersonDto(
                person.id,
                person.firstName,
                person.lastName,
                person.email
            );
        });
    return null;

};

usecases/GetPerson.js

'use strict';

const PersonDto = require("../dtos/PersonDto");

module.exports = async (personId, personRepository) => {

    var person = await personRepository.get(personId);
    if (person != null)
        return new PersonDto(
            person.id,
            person.firstName,
            person.lastName,
            person.email
        );
    return null;

}; 

usecases/CreatePerson.js

'use strict';

const PersonRequestDto = require("../dtos/requests/PersonRequestDto");

module.exports = (request, personRepository) => {

    return personRepository.create(
        new PersonRequestDto(
            request.id,
            request.firstName,
            request.lastName,
            request.email,
            request.password
        )
    );

}; 

usecases/DeletePerson.js

'use strict';

module.exports = (personId, personRepository) => {

    return personRepository.delete(personId);

}; 

usecases/UpdatePerson.js

'use strict';

const PersonRequestDto = require("../dtos/requests/PersonRequestDto");

module.exports = (personId, request, personRepository) => {

    return personRepository.update(personId,
        new PersonRequestDto(
            request.id,
            request.firstName,
            request.lastName,
            request.email,
            request.password
        )
    );
}; 

usecases/LoginPerson.js

'use strict';

module.exports = (login, personRepository) => {

    return personRepository.login(
        new LoginRequestDto(
            login.email,
            login.password
        )
    );

}; 

Dtos

Entity’lerin, Dto’lar üzerinden dışarıya kontrollü şekilde açılmasını sağlıyoruz. Dtos klasörünün ana dizininde response edilecek modeller, requests klasöründe de gelen taleplere karşılık gelecek modellerimiz olacak.

dtos/PersonDto.js => Geriye dönecek Person bilgilerini bu model üzerinden döndüreceğiz.

'use strict';

module.exports = class {
    constructor(id = null, firstName, lastName, email) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
    }
};

dtos/ResponseDto.js => Api katmanı için geriye döneceğimiz base modelimiz.

'use strict';

module.exports = class {
    constructor(message, data, statusCode) {
        this.message = message;
        this.data = data;
        this.statusCode = statusCode;
    }
};

dtos/AuthDto.js => Authentication işlemi sonrasında geriye dönecek modelimiz.

'use strict';

module.exports = class {
    constructor(token) {
        this.token = token;
    }
};

dtos/requests/PersonRequestDto.js => Person ekleme yada güncelleme işlemlerinde gelen requesti karşılayacak modelimiz.

'use strict';

module.exports = class {
    constructor(id = null, firstName, lastName, email, password) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
        this.password = password;
    }
};

Son durumda Core katmanımız aşağıdaki gibi olmalıdır.

Buraya kadar bir özet geçmek gerekirse; Core katmanımızı hiçbir bağımlılık üretmeden tamamlamış olduk.

PersonRepository’mizde sadece Person’ın ne yapabileceğiyle ilgileniyoruz, nasıl yaptığı bizi ilgilendirmiyor.

Aynı şekilde Usecase senaryolarımızı tamamladık. Beklediğimiz çıktıları ve içeriye girecek modelleri biz belirledik. Sonrasında hangi veri tabanı, hangi framework kullanıldığı bizi ilgilendirmiyor.

Bir sonraki yazımda hazırladığımız Core katmanı üzerine, veritabanı ve frameworklerimizi ekleyip API oluşturma kısmına geçeceğiz.

Okuduğunuz için teşekkürler.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir