Pendekatan Arsitektural Solusi ASP.NET MVC (3) - Service Layers

Sebelumnya saya sudah membahas penggunaan Repository pattern dalam ASP.NET MVC. Dalam posting kali ini saya akan membahas tentang Service Layer, sebuah architectural pattern lain yang cukup terkenal selain Repository pattern. Pattern ini juga disebutkan oleh Martin Fowler dalam bukunya Pattern of Enterprise Application Architecture. Seperti dalam membahas Repository pattern, saya tidak akan banyak menguraikan teori di sini.

Dalam konteks pembahasan ASP.NET MVC controllers, Repository, dan Service Layers, secara diagram saya menggambarkan arah dependency-nya sebagai berikut:

Jadi, Controllers punya dependensi terhadap Service Layers, dan Service Layers punya dependensi terhadap Repositories. Perhatikan bahwa Controllers tidak secara langsung punya dependensi terhadap Repositories, melainkan melalui Service Layers. Lalu kenapa kita menempatkan Service Layers sebagai middle-man di situ? Bukannya dari Controllers bisa langsung memakai Repositories?

Sesuai dengan namanya, Service Layers menyediakan service bagi component atau aplikasi consumer-nya, dalam hal ini ASP.NET MVC Controllers. Tentu saja jenis layanannya bisa bermacam-macam, tidak hanya berhubungan dengan task-task CRUD (Create, Retrieve, Update, dan Delete) data ke database saja yang nantinya akan dilakukan oleh Repositories. Misalnya aplikasi kita butuh untuk mengirimkan email, kita bisa membuat Service Layer yang melakukan pengiriman email ini. Ada lagi misalnya kebutuhan untuk menghubungkan aplikasi kita dengan SharePoint, SAP, Siebel, atau barang-barang mainan enterprise lain, kita juga bisa menggunakan Service Layer.

Oleh karena itu saya melihat Service Layer ini sebagai tempat yang cocok untuk meletakkan business logic. ASP.NET Controllers sesuai dengan best practice dan guideline dari Microsoft sendiri harus dibuat se-thin mungkin, dan bukan tempat yang tepat untuk meletakkan business logic. Repositories di sisi lain, sesuai dengan namanya hanya melakukan task-task yang berhubungan dengan underlying data mechanism. Meletakkan business logic dan fungsi-fungsi lain yang tidak berhubungan dengan data mechanism tentu saja tidak cocok.

OK, sekarang saya akan tambahkan Service Layers ini dalam project Models dari solusi ASP.NET MVC yang sudah kita buat sebelumnya. Penempatan Service Layers ini bisa dilakukan dalam satu project tersendiri atau jadi satu project bersama Repositories. Untuk contoh berikut ini saya menempatkannya dalam satu project bersama Repositories.


Berikut contoh source code dari IContactService dan ContactService. Dalam contoh tersebut selain melakukan task CRUD melalui repository yang berkaitan, service layer ini perform task lain berupa pengiriman email. 

// Copyright (C) 2009 by Agus Suhanto [agus.suhanto@mvps.org]

// For more information please visit http://suhanto.com

//

 

#region

 

using CompanyProfile.Models.Entities;

 

#endregion

 

namespace CompanyProfile.Models.Services

{

    public interface IContactService : ISettingAwareService

    {

        void SendContactMessage(ContactMessage message);

    }

}

 

// Copyright (C) 2009 by Agus Suhanto [agus.suhanto@mvps.org]

// For more information please visit http://suhanto.com

//

 

#region

 

using System;

using System.Text;

using CompanyProfile.Models.Entities;

using CompanyProfile.Models.Helpers;

using CompanyProfile.Models.Properties;

using CompanyProfile.Models.Repositories;

 

#endregion

 

namespace CompanyProfile.Models.Services.Impl

{

    public class ContactService : IContactService

    {

        private readonly IMessageRepository messageRepository;

 

        public ContactService(IMessageRepository messageRepository)

        {

            this.messageRepository = messageRepository;

        }

 

        #region IContactService Members

 

        public Setting Setting { get; set; }

 

        public void SendContactMessage(ContactMessage message)

        {

            if (Setting == null) throw new ArgumentException(Resource.SettingIsNull);

 

            message.MessageId = Guid.NewGuid();

            message.MessageDate = DateTime.Now;

            messageRepository.SaveMessage(message, Setting);

 

            MailHelper.SendEmail(

                Setting.MailFrom,

                Setting.MailTo,

                string.Format(Resource.ContactFormEmailSubject, message.FromName),

                ConstructMailBody(message),

                Setting.SmtpServer,

                Setting.SmtpPort,

                Setting.SmtpRequiresAuth,

                Setting.SmtpUserName,

                Setting.SmtpPassword,

                true /* isAsync */);

        }

 

        #endregion

 

        private static string ConstructMailBody(ContactMessage message)

        {

            var sb = new StringBuilder();

            sb.AppendFormat(Resource.ContactFormFromName, message.FromName);

            sb.AppendFormat(Resource.ContactFormFromEmail, message.FromEmail);

            sb.AppendFormat(Resource.ContactFromSubject, message.Subject);

            sb.AppendFormat(Resource.ContactFormBody, message.Message);

 

            return sb.ToString();

        }

    }

}

OK sekarang kita akan menggunakan ContactService tersebut dalam controller kita yang namanya secara kebetulan sama yaitu ContactsController. Berikut ini source code untuk ContactsController.

// Copyright (C) 2009 by Agus Suhanto [agus.suhanto@mvps.org]

// For more information please visit http://suhanto.com

//

 

#region

 

using System.Web.Mvc;

using CompanyProfile.Models.Entities;

using CompanyProfile.Models.Helpers;

using CompanyProfile.Models.Services;

using CompanyProfile.Web.Properties;

 

#endregion

 

namespace CompanyProfile.Web.Controllers

{

    [HandleError]

    [CultureIdFilter]

    public class ContactsController : ControllerBase

    {

        private readonly IContactService contactService;

 

        public ContactsController(ISettingService settingService,

            IContactService aboutService) : base(settingService)

        {

            contactService = aboutService;

            contactService.Setting = Setting;

        }

 

        public ActionResult Index()

        {

            return View("index", GetLayout());

        }

 

        [AcceptVerbs(HttpVerbs.Post)]

        public ActionResult Submit(ContactMessage message)

        {

            #region Validation

            if (message.FromName.Trim().Length == 0)

                ModelState.AddModelError("FromName", Resource.NameIsRequired);

            if (message.FromEmail.Trim().Length == 0)

                ModelState.AddModelError("FromEmail", Resource.EmailIsRequired);

            if (!message.FromEmail.Trim().IsValidEmail())

                ModelState.AddModelError("FromEmail", Resource.EmailIsInvalid);

            if (message.Subject.Trim().Length == 0)

                ModelState.AddModelError("Subject", Resource.SubjectIsRequired);

            if (message.Message.Trim().Length == 0)

                ModelState.AddModelError("Message", Resource.MessageIsRequired);

 

            if (!ModelState.IsValid) return View("index", GetLayout());

            #endregion

 

            contactService.SendContactMessage(message);

            return RedirectToRoute("Default",

                new { controller = "Contacts", action = "Thanks" });           

        }

 

        public ActionResult Thanks()

        {

            return View("thanks", GetLayout());

        }

    }

}

OK, that's it! Cukup mudah dan sederhana mengimplementasikan Service Layers dalam solusi ASP.NET MVC.

Share this post: | | | |
Published Friday, May 08, 2009 3:05 PM by Agus Suhanto
Filed under: ,

Comments

# re: Pendekatan Arsitektural Solusi ASP.NET MVC (3) - Service Layers

Friday, May 08, 2009 3:57 PM by ronaldwidha

wah series yang bagus. saya sangat enjoy bacanya

> Oleh karena itu saya melihat Service Layer ini sebagai tempat yang cocok untuk meletakkan business logic.

saya kurang setuju dengan pernyataan ini. Tergantung definisi dari business logic itu sendiri sih, tapi kalo business logic in the strictest sense, bukankah dia harus disimpan di model. model tanpa business rules akan berubah menjadi anti pattern: Anemic Domain Model yang harus dihindari. Model akan menjadi sebatas Data Transfer Object saja.

Mungkin yang Agus maksud di sini adalah semacam business process workflow?

Powered by Community Server (Commercial Edition), by Telligent Systems