SprinMVC ve MongoDB 3: Servis Katmanı
SpringMVC ve MongoDB
Daha önceki yazılarda Rest servislerinin nasıl geliştirilebileceği üzerine konuşmuştuk. Bu sefer RDB yerine NoSQL, JPA yerine Spring Data ve Jersey yerine de SpringMVC kullanacağız. Ek olarak en temel halindeki rest servis mimarisini değil daha gelişmiş bir Rest servis mimarisini kullanacağız. Fakat bu servis mimarisi Hateoas kadar gelişmiş olmayacak. Gelişmişlik açısından Hateoas’ın bir tık altını hedef alacağız. Ayrıca uygulamamızı cloud servislere deploy edeceğiz. Cloud Servis sağlayıcısı olarak heroku kullanacağız. Projenin paket yönetimi yine maven yönetecek, test ve canlı ortamımızda servlet container olarak Jetty kullanacağız.
Yazı dizisinin özeti aşağıdaki gibidir.
- Projenin Hazırlanması
- Projenin Yaratılması
- Genel Proje Ayarları
- Heroku’da Proje Nasıl Yaratılır?
- MongoLab – Cloud Servis Olarak MongoDB
- MongoDB ve SpringData ile DAO Katmanı
- SpringData ile Modeller
- SpringData’da DAO Katmanı
- SpringData ve MongoDB Erisim Ayarları
- DAO Testleri ve Fongo
- Servis Katmanı
- Servis Katmanı Nasıl Olmalı?
- Mockito ile Servis Testlerinin Yazılması
- SpringMVC ile Rest Servisleri
- Rest Servisi – Yani Controller
- SpringMVC Ayarları Nasıl Yapılır?
- SpringMVC Testleri ve JsonPath
SprinMVC ve MongoDB 3: Servis Katmanı
DAO Katmanımızı yazdığımıza göre şimdi servis katmanımızı geliştirebiliriz. Basit bir uygulamamız olduğundan servis katmanımız da sorun olmayacaktır.
Servis Katmanı Nasıl Olmalı?
Öncelikle src/main/java altına com.bahadirakin.service ismindeki paketimizi yaratıyoruz. Sonra buraya ilk ve tek servis arayüzümüzü yani UserService interface’imizi yaratıyoruz
UserService.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | package com.bahadirakin.service; import com.bahadirakin.model.User; import java.util.List; /** * Created by bhdrkn on 08/02/15. */ public interface UserService { User createUser(final User user); User updateUser(String id, final User user); User deleteUser(String id, final User user); User findUserById(final String id); List<User> getAllUsers(); User authenticate(final String username, final String password); void sendForgottenUsernameAndPasswordEmail(final String email); } |
Burada DAO katmanından yukarı taşımak istediğimiz tüm metodları taşıyoruz. Eğer bir validasyon ya da exception handling yapılacaksa burada yapılabilir. DAO katmanımız sadece Interface olduğundan exception handling’e çok yer tanımıyor. Fakat üst servislerden ya da rest servislere göndereceğimiz exceptionları yine burada tanımlıyoruz. Interface’imizi tanımlamamız bittikten sonra implementasyonunu yazmaya geçebiliriz. Bunun için yine aynı paketin altına UserServiceImpl isminde bir sınıf yaratıyoruz. (Normal şartlarda arayüz başka pakette, implementasyon başka pakette olmasında yarar vardır.)
UserServiceImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | package com.bahadirakin.service; import com.bahadirakin.dao.UserRepository; import com.bahadirakin.model.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; /** * Created by bhdrkn on 08/02/15. */ @Service public class UserServiceImpl implements UserService { private final UserRepository userRepository; @Autowired public UserServiceImpl(UserRepository userRepository) { this.userRepository = userRepository; } @Override public User createUser(User user) { return userRepository.save(user); } @Override public User updateUser(String id, User user) { final User dbUser = userRepository.findOne(id); dbUser.setUsername(user.getUsername()); dbUser.setPassword(user.getPassword()); dbUser.setEmail(user.getEmail()); return userRepository.save(dbUser); } @Override public User deleteUser(String id, User user) { final User dbuser = this.authenticate(user.getUsername(), user.getPassword()); if(dbuser== null || dbuser.getId().equals(id) == false){ throw new RuntimeException("Wrong User"); } userRepository.delete(user.getId()); return user; } @Override public User findUserById(String id) { return userRepository.findOne(id); } @Override public List<User> getAllUsers() { return userRepository.findAll(); } @Override public User authenticate(String username, String password) { return userRepository.findByUsernameAndPassword(username, password); } @Override public void sendForgottenUsernameAndPasswordEmail(String email) { final User user = userRepository.findByEmail(email); // Sends email etc. } } |
Burada dikkat etmenizi istediğim kısımlar
- Service annotasyonu kullanıldı ama herhangibir transaction bilgisi bildirilmedi. Bunun sebebi nosql veritabanı kullanmamızdır.
- Servisleriniz sadece veriyi çekip göstermek için değil, validasyon da exception handling te burada yapılıyor. Örnek basit olduğundan gereksiz bir katman gibi gözükebilir ama iş mantığınız bu katmanda yer alacak. Örneğin “şifremi unuttum” işlemi için hem veritabanı işlemi hem mail gönderim işlemi yapılmalı.
Şimdi ise bu paketin tarandığına emin olmak için applicationContext.xml’i güncellememiz gerekiyor.
applicationContext.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"> <import resource="classpath*:springDataContext.xml"/> <context:component-scan base-package="com.bahadirakin.service"/> </beans> |
Güncellememizi yaptığımıza göre testlerimizi yazmaya geçebiliriz.
Mockito ile Servis Testleri
Servis testlerimiz sırasında veritabanı ayağa kaldırmak her ne kadar Fongo kullansak bile maliyetli olacaktır. Bu sebepten başka bir yapı kullanacağız. DAO bağımlılıklarımızı mocklayarak devam edeceğiz. Mocklama işlemi için Mockito kullanacağız. Mockito bağımlılığımızı projemizi ayarlarken vermiştik.
Bunun için src/test/java dizinin altına com.bahadirakin.service isminde paket yaratıyoruz. Daha sorna bu paketin altına UserServiceTest isminde java sınıfımızı yaratıyoruz.
UserServiceTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | package com.bahadirakin.service; import com.bahadirakin.dao.UserRepository; import com.bahadirakin.model.User; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; /** * Created by bhdrkn on 08/02/15. */ @RunWith(JUnit4.class) public class UserServiceTest { @Mock private UserRepository userRepository; private UserService userService; @Before public void init(){ MockitoAnnotations.initMocks(this); userService = new UserServiceImpl(userRepository); } @Test public void testAuthentication(){ final String username = "bhdrkn"; final String password = "1q2w3e"; final User mockUser = new User(); mockUser.setId("1"); mockUser.setUsername(username); mockUser.setPassword(password); mockUser.setEmail("bhdrkn@gmail.com"); Mockito.when(userRepository.findByUsernameAndPassword(username,password)).thenReturn(mockUser); final User user = userService.authenticate(username, password); Assert.assertNotNull(user); } @Test public void testUnsuccessfulAuthentication(){ final String username = "bhdrkn"; final String password = "1q2w3e"; Mockito.when(userRepository.findByUsernameAndPassword(username,password)).thenReturn(null); final User user = userService.authenticate(username, password); Assert.assertNull(user); } } |
Burada dikkat etmenizi istediğim noktlar ise aşağıdaki gibidir:
- Biz UserService sınıfını test ediyoruz. Bu sebepten onun bağımlı olduğu diğer tüm sınıfları mockluyoruz. Mockladığımız sınıfları da gördüğünüz gibi @Mock annotasyonuyla belirtiyoruz.
- Mock annotasyonuyla belirtilen sınıfların oluşması için MockitoAnnotations.initMocks(this); metodunun çağırılması gerekmektedir.
- Biz springten bağımsız olarak test yazdığımız için mockladıklarımızı otomatik olarak inject edemiyoruz. Bu sebepten kendimiz yaratıyoruz ve yaratırken parametre olarak mockladığımız nesneyi aktarıyoruz.
Son
Basit olması açısından sadece authentication kısmının unit testlerini yazdım. Arzu ederseniz diğer kısımların unit testlerini siz tamamlayabilirsiniz.
Arzu ederseniz projenin son halinin kaynak kodlarına Git adresi üzerinden erişebilirsiniz.
End Of Line