![]() |
|
|
|||||||
| .NET I detta forum diskuteras ämnen som berör .NET-plattformen, bland annat asp.net. |
![]() |
|
|
Trådverktyg | Visningsalternativ |
|
|
#1 | |
|
Medlem
Registrerad: 2005-12-15
Ort: Linköping
Inlägg: 196
Lösningar: 0 |
Entities, Services och Repositories
Hejsan,
För länge sedan så fick jag detta svar i en tråd av Nickemannen när jag frågade om arkitektur och upplägg av mina projekt. Citat:
Entities har jag ganska klart för mig vad det är men i mitt sökande så har jag stött på något som de har kallat DomainObjects. Det har verkat som om DomainObjects är nästintill samma sak som Entities? Kod:
namespace DomainObjects {
public partial class Comment : IComment {
public IUser ByUser{
get { return User; }
}
public string FromIPAddress {
get { return IPAddress; }
}
}
}
Sedan så har jag fattat det som att Repositories är något liknande detta? Kod:
public class CommentRepository : ICommentRepository {
public Comment GetById(int id) {
// O/R Mapper
}
public void Add(Comment comment) {
// O/R Mapper
}
}
Och sist, vad ska jag ha i Services? Hoppas jag har formulerat mig bra, tack på förhand! Timmie |
|
|
|
|
|
|
#2 |
|
Moderator
Registrerad: 2000-08-20
Ort: Kungälv (Göteborg)
Inlägg: 3 467
Lösningar: 42 |
Domain object = entity
![]() Domain object kommer från Domain Driven Design där objekten beskriver domänområdet som det löser. Som du beskriver med repository kan man göra, alternativet är också att strunta i repositoryn och använda sig av en or/mappers context direkt. (ex nhibernate session). I just det fallet du har där så tillhör ju en comment någonting antingen en bloggpost eller artikel i sådana fall så använder man sig inte av en repository för comment, utan den bör kanske sparas med bloggposten eller artikeln. Dvs varje entitet behöver inte betyda en repository/service eftersom den kanske sparas ihop med någon annan entitet. |
|
|
|
|
|
#3 | |
|
Medlem
Registrerad: 2005-12-15
Ort: Linköping
Inlägg: 196
Lösningar: 0 |
Citat:
|
|
|
|
|
|
|
#4 | ||
|
Medlem
Registrerad: 2005-02-03
Ort: Stockholm
Inlägg: 62
Lösningar: 16 |
Citat:
Jag tror inte att "Domänservicen" ovan syftade på service lagret. Det finns nämligen ett lager som ibland brukar kallas Service Layer (t.ex. enligt Martin Fowlers bok Patterns of Enterprise Application Architecture) och i det lagret så finns det förstås Services. Sedan finns det ett lager som kallas Domain Layer (t.ex. enligt Eric Evans bok Domain-Driven Design (DDD)) och där ingår också något som kallas Service. Således kan man säga att det finns både "Service Layer Services" och "Domain Layer Services". Det förstnämnda blir dock lite väl mycket tårta på tårta, och därför är det lämpligare att använda sig av begreppet Application Layer Services, vilket är ett begrepp som bl.a. Eric Evans använder sig av i sin DDD-bok. Fowler's Service Layer är alltså synonymt med Evans Application Layer, enligt Fowler själv på hans sida om antimönstret anemiska domänmodellen. En Service kan f.ö. finnas på andra lager utan att man anser den tillhöra någon av Domain Layer eller Service/Application Layer, t.ex. ett Infrastructure Layer kan innehålla en service för att skicka email. Den kanske främsta gemensamma nämnaren för alla services i olika layers är nog att de är tillståndslösa (stateless). När det gäller en Domain Layer Service så används sådana för att placera logik som inte naturligt hör hemma i någon entity eller value object i domänlagret. Det bör således inte vara en utgångspunkt att försöka sträva efter att skapa domän services (för då är det risk att resultatet blir en tillämpning på det anti-pattern som Fowler kallar Anemic Domain Model (se länk ovan). Man bör snarare sträva efter att placera logiken på en entity eller ett value object där logiken hör hemma bäst, men om man inte lyckas hitta något sådant, t.ex. för att man behöver använda data från olika klasser (entities/values) och inte vill skapa beroende från den ena till den andra, då kan man placera metoden i en Domain Service i stället. En Application/Service Layer Service däremot består av mer use-case-orienterade metoder på en högre nivå, dvs en tidigare nivå i anropskedjan innan domänlagret, och är det första user-interface-oberoende lagret som alltså kan anropas från olika typer av klientkod. Exempelvis bör man kunna återanvända ett Application/Service Layer från en ASP.NET Page, eller från Windows Forms, eller från en Web Service, efter att man förstås har transformerat bort alla klient-specifika saker, t.ex. bör inte några request-objekt eller windows-objekt förekomma i metodsignaturer i application/service lagret. Från en application layer service metod kan man sedan bl.a. använda sig av domänobjekten (t.ex. entity, value objects, domain services, factories, repository och specification). Som sagt var, terminologin är inte alltid riktig enhetlig där samma namn alltid används för samma sak, så som i det ovan nämnda fallet med att "Evans Application Layer = Fowler Service Layer". Nedan följer några andra jämförelseer man kan göra, och som kan vara intressanta reflektioner för den som har läst (eller kommer att läsa) Larman-boken med GRASP patterns : * GRASP Controller (som beskrivs som det första objektet efter UI layer) beskrivs på ett sådant sätt att den påminner starkt om Application/Service Layer * GRASP Information Expert beskrivs som det objekt som innehåller informationen, och därför är en ofta naturlig utgångspunkt när det gäller var en metod ska placeras (förutom att även beakta övriga GRASP-principer såsom low coupling och high cohesion) och DDD-motsvarigeten är Entity och Value Objects, som är koncept från domänen/verksamheten som innehåller information och därför är lämpliga att placera relaterade metoder som behöver använda informationen * GRASP Pure Fabrication är ett objekt som inte motsvarar något koncept i domänen, och där man kan placera metoder om det inte fanns någon lämpligare Information expert. DDD-motsvarigheten är Domain Service dvs där placerar man metoder om de inte passade bättre till någon Entity eller Value Object. |
||
|
|
|
|
|
#5 |
|
Medlem
Registrerad: 2005-12-15
Ort: Linköping
Inlägg: 196
Lösningar: 0 |
Tack så mycket TomasJ för det utförliga svaret! Jag ska ta och läsa på lite mer om vad de olika begreppen innebär men tror att det är Application Layer Services som jag syftade på då jag har varit inne väldigt mycket på DDD.
Jag behöver dock lite hjälp när det gäller det praktiska. Jag vet inte om jag har förstått allt helt rätt när det kommer till repository och interfaces då jag är helt ny på de begreppen. Har dock väldigt svårt för att förklara hur jag tänker men ska försöka göra mitt bästa.. När jag har kollat runt lite om repository så har jag fattat det som det jag skrev i mitt huvudinlägg och det verkar vara korrekt. På projektet som jag hade tänkt att använda mig utav den här typen av uppdelning (säger man så?) så är jag fast vid .NET 3.5 och kan inte använda mig Linq eller dylikt då loopa inte stödjer det. Min tanke var då att ha ett interface för varje repository som var plaserat i assemblyn MyApp.Core, hade även tänkt att ha entity interfaces där. Mina repositorys skulle sen med all logik skulle ligga under assemblyn MyApp.Infrastructure.MySQL. Detta trodde jag var en smart idé för så som jag förstod det som så behövde jag bara göra om min MyApp.Infrastructure.MySQL om jag ville använda mig utav EF, NHibernate osv i framtiden. Som sagt jag kan ha tänkt fel med så här gjorde jag: Kod:
using MyApp.Core.DomainObjects;
namespace MyApp.Core.Repository {
public interface IUserRepository : IRepository<IUser> {
IUser GetById(int id);
}
}
Kod:
// ...
using MyApp.Core.Repository;
using MyApp.Core.DomainObjects;
using MyApp.Infrastructure.MySQL.DomainObjects;
namespace MyApp.Infrastructure.MySQL.Repository {
public class UserRepository : IUserRepository {
// ...
public virtual IUser GetById(int id) {
// Enbart för att testa
return new User {
Id = 1,
};
}
// ....
}
}
Kod:
using MyApp.Core.Repository;
using MyApp.Core.DomainObjects;
namespace MyApp.Web {
public class UserController : Controller {
private readonly IUserRepository _userRepository;
public UserController(IUserRepository userRepository) {
_userRepository = userRepository;
}
public ActionResult Index() {
IUser user = _userRepository.GetById(1);
// ..
}
}
}
Hoppas jag har formulerat mig så ni förstår med min svengelska. |
|
|
|
|
|
#6 |
|
Medlem
Registrerad: 1999-12-28
Ort: Lund
Inlägg: 4 593
Lösningar: 57 |
Korta kommentarer, läste inte så noga, men GetById är en metod som behöver finnas på varje / samt endast på dina aggregate roots, så man kan ju göra basrepot generiskt och endast implementera specifika aggregate root frågor på respektive roots repository.
Behöver du verkligen entity interfaces, jag tycker det brukar göra det hela överkomplext och det är inte ofta jag sett nyttan med det, utan det har snarare ökat på komplexiteten, och ju högre komplexitet, ja ni vet. Andra saken är att jag skulle rekommendera att du har typade vyobjekt för MVCn som du fyller med info från repona, där kan verktyg som AutoMapper hjälpa dig för att slippa skriva mappningen helt och hållet själv. Domänentiteter är inte tänkta för att visas i UI, ofta har UI_entiteter helt annat beteende och konstruktion än ett domänobjekt. |
|
|
|
|
|
#7 | ||
|
Medlem
Registrerad: 2005-12-15
Ort: Linköping
Inlägg: 196
Lösningar: 0 |
Citat:
Jag måste ha missuppfattat något för jag får inte detta att fungera utan får enbart felmeddelandet: Citat:
Kod:
using MyApp.Core.DomainObjects;
using MyApp.Core.Service;
using MyApp.Core.Repository;
namespace MyApp.Web {
public partial class _Default : System.Web.UI.Page {
private IUserRepository _userRepository;
public _Default() { }
public _Default(IUserRepository userRepository) {
_userRepository = userRepository;
}
public void Page_Load(object sender, EventArgs e) {
IUser user = _userRepository.GetById(1);
Response.Write(user.Id.ToString());
}
}
}
|
||
|
|
|
|
|
#8 |
|
Moderator
Registrerad: 2000-08-20
Ort: Kungälv (Göteborg)
Inlägg: 3 467
Lösningar: 42 |
Använder du dig av Asp.NET mvc finns det ingen codebehind, utan du binder datan i din View mot det som retuneras i ViewResult.
Retunerar du en lista med Customers så kan du köra en for loop och loopa ut dina customers. |
|
|
|
|
|
#9 |
|
Medlem
Registrerad: 2005-12-15
Ort: Linköping
Inlägg: 196
Lösningar: 0 |
Det är ju dock det jag inte gör då loopia inte stödjer det så jag är fast vid WebForms..
|
|
|
|
|
|
#10 |
|
Medlem
Registrerad: 2005-12-15
Ort: Linköping
Inlägg: 196
Lösningar: 0 |
har läst på lite mer och för att få det som jag vill göra att fungera så måste jag använda mig utav dependency injections och ioc eller hur?
|
|
|
|
|
|
#11 |
|
Moderator
Registrerad: 2000-08-20
Ort: Kungälv (Göteborg)
Inlägg: 3 467
Lösningar: 42 |
Japp om du hade använt just ASP.NET MVC hade du overridat standard ControllerFactoryn med din egen som hade byggt upp alla Controllers med en DI container.
Med ASP.NET vet jag inte hur, men det bör gå på något liknande sätt. |
|
|
|
|
|
#12 |
|
Medlem
Registrerad: 2005-11-24
Inlägg: 5
Lösningar: 0 |
WebForms i sig är ingen höjdare när det gäller Dependency Injection. För att få WebForms att spela "fint" med DI kan du använda dig av MVP (Model-View-Presenter) alternativt använda din DI-container (dvs, den 3de-parts komponent du använder för att säga till hur dina objekt skall "sättas" ihop, jag brukar använda StructureMap men det finns en uppsjö olika alternativ) som en Service Locator (http://msdn.microsoft.com/en-us/library/cc707905.aspx), vilket inte är best practice, men är man fast i WebForms så är man
|
|
|
|
|
|
#13 |
|
Medlem
Registrerad: 2005-12-15
Ort: Linköping
Inlägg: 196
Lösningar: 0 |
Tack så mycket Krezni. Dock så gjorde jag valet att byta webbhotell till Binero istället då jag är så förbannat trött på WebForms.
Jag har nu ett litet problem. Jag använder mig utav SubSonic SimpleRepository för att hämta data i mina repositorys. Trots att erka förespråkade att man inte skulle ha entity interfaces så har jag valt att använda det då jag ibland behöver lägga till vissa subsonic attributs i mina entitys. Sitter just nu på jobbet så har inte hela koden framför mig eller felmeddelandet men ni förstår nog vad det blir för fel och förhoppningsvis så kan ni hjälpa mig att lösa felet. Jag behöver göra om mina vanliga entiteter till mina interfaces entiteter och jag vet inte hur man gör eller hur man löser det på ett annat sätt. I dagsläget så gör jag typ såhär: Kod:
public IUser FindById(int id) {
return _repository.Single<User>(x=>x.Id==id);
}
Jag försökte att göra om det värdet som skickas tillbaka till IUser men då så hittade den inte mina entiteter i min Model sen. (var user = _repost.... return (IUser)user) Det är luddigt förklarat som vanligt när det kommer ifrån mig men hoppas ni förstår vad jag menar och kan hjälpa mig. Vänligen, Timmie |
|
|
|
|
|
#14 |
|
Moderator
Registrerad: 2000-08-20
Ort: Kungälv (Göteborg)
Inlägg: 3 467
Lösningar: 42 |
Jag antar att ditt problem är att User entiteten inte implemmenterar av interfacet IUser.
Vad har du för nytta just nu av att ha ett interface för User? |
|
|
|
|
|
#15 |
|
Medlem
Registrerad: 2005-12-15
Ort: Linköping
Inlägg: 196
Lösningar: 0 |
Hur menar du med att den inte implemmenterar av interfacet IUser?
Jag använder mig utav ett interface för att kunna köra subsonic attributes i mina entiteter, t.ex: Kod:
public class User : IUser {
public int Id { get; set; }
[SubSonic.SubSonicStringLength(100)]
public string UserName { get; set; }
}
|
|
|
|
|
|
#16 |
|
Medlem
Registrerad: 2005-12-15
Ort: Linköping
Inlägg: 196
Lösningar: 0 |
Ursäkta min dubbelpost men nu är jag hemma och kan ge lite mer om problemet.
Jag har nu försökt att göra på detta vis: Kod:
// UserRepository
public IUser FindById(int id) {
return _repository.Single<User>(x => x.Id == id) as IUser;
}
// Controller
public ActionResult Index() {
return View(_userRepository.FindAll());
}
Kod:
System.NullReferenceException: Object reference not set to an instance of an object.
<% foreach (var item in Model) { %>
|
|
|
|
![]() |
| Trådverktyg | |
| Visningsalternativ | |
|
|