Validate date from other bounded context - domain-driven-design

I have two Bounded Context (studentenrollment, courses).
Studentenrollment has all the student with his course ids and his homework.
Courses have the admin part that content all the information related with the course.
When a student want to get information of the course, it hits an endpoint( /courses/ID) sending the jwt token. In the course context I get the student ID, course ID and create query that it's dispatched in the bus. In the query handler before getting the information of the course from the course ID, I want to validate if the student ID exist and this student has this course. For that I have to call the another context bounded studentenrollment. So, I was looking for how to handle that on internet and I found this:
https://medium.com/#martinezdelariva/authentication-and-authorization-in-ddd-671f7a5596ac
class findByCourseIdAndStudentIdQueryHandler()
{
public function handle($findByCourseIdAndStudentIdQuery)
{
$courseId = $findByCourseIdAndStudentIdQuery->courseId();
$studentId = $findByCourseIdAndStudentIdQuery->studentId();
$student = $this->collaboratorService->studentFrom(
$courseId,
$studentId
);
$this->courseRepository->findByCourseId($courseId);
}
}
class collaboratorService()
{
public function studentFrom($courseId, $studentId)
{
$student = $this->studentEnrollmentClient->getStudentFrom($courseId, $studentId);
if (!$student) {
throw new InvalidStudentException();
}
return $student;
}
}
What do you think?
UPDATED
namespace App\Context\Course\Module\Course\UI\Controller;
class GetCourseController extends Controller
{
public function getAction($request) {
$this->ask(new FindByCourseIdQueryHandler($request->get('course_id'));
}
}
namespace App\Context\Course\Module\Course\Infrastracture\Query;
class AuthorizedQueryDispatcher extends QueryDispatcher
{
//In this case $query would be FindByCourseIdQueryHandler
public function handle($query)
{
$authUser = $this->oauthService->getAuthUser();
//it can be student or teacher
$role = $authUser->getRole();
$userId = $authUser->getUserId();
//it will return FindByCourseIdAndStudentIdAuthorizedQueryHandler
$authorizedQuery = $this->inflector->getAuthorizedQueryName->from($userId, $role, $query);
$this->dispatch($authorizedQuery);
$this->queryDispatch->dispatch($query);
}
}
namespace App\Context\Course\Module\Course\Application\Query;
class FindByCourseIdAndStudentIdAuthorizedQueryHandler
{
public function handle($findByCourseIdAndStudentIdQuery)
{
$student = $this->studentEnrollmentClient->getStudentFrom($findByCourseIdAndStudentIdQuery->courseId, $findByCourseIdAndStudentIdQuery->studentId);
if (!$student) {
throw new InvalidStudentException();
}
}
}
namespace App\Context\Course\Module\Course\Application\Query;
class findByCourseIdAndStudentIdQueryHandler()
{
public function handle($findByCourseIdQueryHandler)
{
$courseId = $findByCourseIdQueryHandler->courseId();
$this->courseRepository->findByCourseId($courseId);
}
}

TLDR; Authorization should be clearly separated from the Domain layer, for example in a different package/namespace/module. Also, the dependency from the Domain to the Authorization should be inverted, the Domain should not depend/know about the authorization/
One way to implement it is to create an Authorization service, for example FindByCourseIdAndStudentIdQueryAuthorizer (let's name it Authorizer). This service may cross Bounded context (BC) boundaries, i.e. it could depend on remote domain services from remote BCs. Ideally, the remote data should be already available when the Authorizer does the checking. In this way the system is more resilient in case remote Bounded context services are not available. You can do this by listening to remote events or by background tasks.
Ideally, the domain layer (from any BC) should not know about the Authorizers.
One way to do this is to decorate the QueryDispatcher (or what you have) in the Composition root of the application with an AuthorizedQueryDispatcher. This AuthorizedQueryDispatcher, when it receives a query, it first search an Authorizer and then calls it. If the authorization fails then the query is rejected. If the authorization succedds or there is not authorizer then the query is sent to the real/decorated QueryDispatcher.
If can't do this (i.e. you don't have a QueryDispatcher) then you can try to decorate every query handler (by hand?). For example, you could have a FindByCourseIdAndStudentIdAuthorizedQueryHandler that has the same interface as the FindByCourseIdAndStudentIdQueryHandler. You could replace them in the composition root of the application (DIC).

Related

AutoPopulate attribute not working on AutoQuery DTO

I am trying to get the new AutoPopulate attribute to work but I am having some difficulty understanding the new AutoQuery functionality.
To test it out I am aiming to replace this service that is a standard AutoQuery endpoint but it also filters by the logged in users ID. I want to replace it so it works completely with just the model definition.
public class DevExtremeService : ServiceBase
{
public IAutoQueryDb AutoQuery { get; set; }
public QueryResponse<DeWatchedUrlResponse> Any(WatchedUrlDevExRequest request)
{
var q = AutoQuery.CreateDevXQuery(request, Request.GetRequestParams(), Request);
q.Where(x => x.UserAuthCustomId == GetUserId());
var response = AutoQuery.Execute(request, q, base.Request);
return response;
}
}
[Route("/de/watched-urls")]
public class WatchedUrlDevExRequest : QueryDb<WatchedUrlRecord, DeWatchedUrlResponse>
{
}
So I deleted the service and updated model to:
[ValidateIsAuthenticated]
[AutoPopulate(nameof(WatchedUrlDevExRequest.UserAuthCustomId), Eval = "userAuthId")]
[Route("/de/watched-urls")]
public class WatchedUrlDevExRequest : QueryDb<WatchedUrlRecord, DeWatchedUrlResponse>
{
public long UserAuthCustomId { get; set; }
}
My understanding from reading the release notes is that userAuthId is a variable declared in the AutoQuery #script context that is added by default.
I have tried a few different variations and I cannot get the property to populate. The docs seem focused on audit history and multitenancy but really I am just looking for a quick way to make endpoints.
I have 2 main questions:
Why is the auto populate not working on this property?
Where can I see the default #script definition so I can see how things like userAuthId are defined and better get an understanding how to add my own?
edit
I re-read docs and I gues this only works when writing data to db. I really like the concept of being able to apply #script to a request model via attribute. Is that possible?
AutoQuery CRUD's [AutoPopulate] attribute initially only populated AutoQuery CRUD's Data Model when performing CRUD operations, e.g. Inserting, Updating or Deleting entities.
For ensuring a query only returns a users records, it's recommended to use an AutoFilter instead, which behaves as expected ensuring the query is always applied to the Data Model, e.g:
[ValidateIsAuthenticated]
[Route("/de/watched-urls")]
[AutoFilter(QueryTerm.Ensure, nameof(WatchedUrlRecord.UserAuthCustomId),
Eval = "userAuthId")]
public class WatchedUrlDevExRequest : QueryDb<WatchedUrlRecord, DeWatchedUrlResponse>
{
}
However as I can see it's a useful feature I've also just added support for [AutoPopulate] & [AutoMap] attributes on Query DTOs in this commit where your AutoQuery DTO would work as expected where it populates the Request DTO property:
[ValidateIsAuthenticated]
[AutoPopulate(nameof(WatchedUrlDevExRequest.UserAuthCustomId), Eval = "userAuthId")]
[Route("/de/watched-urls")]
public class WatchedUrlDevExRequest : QueryDb<WatchedUrlRecord, DeWatchedUrlResponse>
{
public long UserAuthCustomId { get; set; }
}
This change is available from v5.10.3 that's now available on MyGet.
An alternative approach to populate AutoQuery's Request DTO you could have a custom AutoQuery implementation like you have, an Extensible Query Filter or custom base class or I'd personally go with a Global Request Filter that updates all Request DTOs with a shared interface, e.g:
GlobalRequestFilters.Add((req, res, dto) => {
if (dto is IHasUserAuthCustomId authDto)
{
var session = req.GetSession();
if (session.IsAuthenticated)
authDto.UserAuthCustomId = session.UserAuthId;
}
});
Or you could wrap this logic in a Request Filter Attribute and apply the behavior to Request DTOs that way.
Note: userAuthId is a ServiceStack #Script method that returns the currently authenticated User Id.

DDD: Syncing bounded contexts causing different domain behavior/logic

I am currently working on a DDD system that is composed out of several bounded contexts. 2 of them are:
Context "account management": Only staff members are allowed to work here. The idea is to manage customer accounts (like address, phone numbers, contacts etc etc) and to verify the account of a customer (basically checking if the data the customer supplied is valid).
Context "website": I can login as a customer and edit my data (change my address for example)
Here is the issue:
A user logged in into the account management context is per definition an employee. So I can assume that changes made here are "trustworthy" in the sense of "the data is verified". A simplified variant of the appservice looks like this:
class AccountAppService
{
public function changeAddress(string $accountId, string $address) : void
{
$account = $this->accountRepository->ofId(new Guid($accountId));
$account->changeAddress(new Address($address));
}
{
This is the appservice I am calling when an employee is changing an address. Note that there is no IdentityService that I inject/use in order to know who the employee is as this is not interesting here. The Account entity would emit an AccountAddressChanged event after successfully calling its changeAddress() method like so
class Account implements Entity
{
public function changeAddress(Address $address) : void
{
$this->address = $address;
DomainEventSubscriber::instance()->publish(new AccountAddressChanged($this));
}
}
But I also need to reflect changes as soon as a customer edits data on the website. I plan to do this async via events a la "AccountAddressChangedViaWebsite". The account management context will subscribe and handle that event, setting the corresponding account to "unverified" again. So a simplified subscriber of the account management context could look like:
class AccountAddressChangedViaWebsiteSubscriber
{
public function handle(AccountAddressChangedViaWebsite $event) : void
{
$accountId = $event->accountId();
$address = $event->getAddress();
$this->accountService->changeAddress($accountId, $address);
}
}
Now the question: Employees call the appservice directly, customers via subscribers. If we say "we have to reverify an account after the customer updates his data" it sounds like a domain concept.
Domain concepts fit into entities or domain services, but not into application services or subscribers for what I know. It implies to me that the following should be avoided (note the last line calling unverifyAccount()):
class AccountAddressChangedViaWebsiteSubscriber
{
public function handle(AccountAddressChangedViaWebsite $event) : void
{
$accountId = $event->accountId();
$address = $event->getAddress();
$this->accountService->changeAddress($accountId, $address);
$this->accountService->unverifyAccount($accountId);
}
}
This is domain logic that is somewhat hidden in a subscriber which seems odd. I have the gut feeling that this should be the responsibility of a domain service, but how would the domain service know that it is called by an external event (via subscriber) or a command?
I could pass a sort of "Originator" ValueObject that tells me wheter the user causing this is an employee or an external system. Example:
class OriginatorService
{
public function changeAddress(Originator $originator, Account $account, Address $address) : void
{
$account->changeAddress($address);
if(($originator instanceof Employee) === false) {
$account->unverify();
}
}
}
Here I delegate the responsibility of what to do to a domain service. But might double dispatching the OriginatorService into the Account entity be a good solution? This way the entity could check who caused the change via asking the passed in originatorService and could unverify itself.
I guess I am going down the DDD rabbit hole here, but what are your experiences/best practises in such a case?
The simplest answer is probably introduce UnverifiedAddress as a concept in your model, rather than trying to treat "Address" as a universal idea with the verification bolted on as an afterthought.

CQRS in data-centric processes

I have got a question related to CQRS in data centric processes. Let me explain it better.
Consider we have a SOAP/JSON/whatever service, which transfers some data to our system during an integration process. It is said that in CQRS every state change must be achieved by the means of commands (or events if Event Sourcing is used).
When it comes to our integrating process we have got a great deal of structured DATA instead of a set of commands/events and I am wondering how to actually process those data.
// Some Façade service
class SomeService
{
$_someService;
public function __construct(SomeService $someService)
{
$this->_someService = $someService;
}
// Magic function to make it all good and
public function process($dto)
{
// if I get it correctly here I need somehow
// convert incoming dto (xml/json/array/etc)
// to a set of commands, i. e
$this->someService->doSomeStuff($dto->someStuffData);
// SomeStuffChangedEvent raised here
$this->someService->doSomeMoreStuff($dtom->someMoreStuffData);
// SomeMoreStuffChangedEvent raised here
}
}
My question is whether my suggestion is suitable in the given case or there may be some better methods to do what I need. Thank you in advance.
Agreed, a service may have a different interface. If you create a rest-api to update employees, you may want to provide an UpdateEmployeeMessage which contains everything that can change. In a CRUD-kind of service, this message would probably mirror the database.
Inside of the service, you can split the message into commands:
public void Update(UpdateEmployeeMessage message)
{
bus.Send(new UpdateName
{
EmployeeId = message.EmployeeId,
First = message.FirstName,
Last = message.LastName,
});
bus.Send(new UpdateAddress
{
EmployeeId = message.EmployeeId,
Street = message.Street,
ZipCode = message.ZipCode,
City = message.City
});
bus.Send(new UpdateContactInfo
{
EmployeeId = message.EmployeeId,
Phone = message.Phone,
Email = message.Email
});
}
Or you could call the aggregate directly:
public void Update(UpdateEmployeeMessage message)
{
var employee = repository.Get<Employee>(message.EmployeeId);
employee.UpdateName(message.FirstName, message.LastName);
employee.UpdateAddress(message.Street, message.ZipCode, message.City);
employee.UpdatePhone(message.Phone);
employee.UpdateEmail(message.Email);
repository.Save(employee);
}

Extract paging from IQueryable

I'm using a function to allow query composition from Web UI and I would to implement paging functionality which it will be available for dataBound controls such as ObjectDataSource, gridView, etc:
public class MyClass<TEntity> where TEntity : class
{
FakeEntities xxx = new FakeEntities();
public IEnumerable<TEntity> Get(Func<IQueryable<TEntity>, IQueryable<TEntity>> queryExpression)
{
var query = xxx.Set<TEntity>();
return queryExpression(query).ToList();
}
public int Count()
{
// What Can I return?
}
}
// **** USAGE ****
MyClass<User> u = new MyClass<User>();
var all = u.Get(p => p.Where(z => z.Account == "Smith").OrderBy(order => order.IdOther).Skip(1).Take(2));
The above query use Take and Skip function, so can I get real count of my entities? Obviously I must return Query Count without modifying filter expression.
I found this solution: Get count of an IQueryable<T>
However I get targetInvocationException with inner message {"This method supports the LINQ to Entities infrastructure and is not intended to be used directly from your code."}
I know my request could be freak-abnormal, because best practice should to impose to move "presentation needs" to some wrap class and that's is what I'll do. So I don't need anymore to get Count entities on my business logic class.
That's just UI concern only.
Thank you the same.

Using a RequestFilter to Perform Custom Authentication in ServiceStack

Brand new to ServiceStack, so forgive me if this is an easy one.
I am writing an API that will use a custom HTTP header for authentication information. I've added a RequestFilter like so:
RequestFilters.Add((httpReq, httpResp, requestDto) =>
{
if(httpReq.Headers["MyMagicHeader"] != "magic")
{
throw HttpError.Unauthorized("Unauthorized");
}
else
{
//TODO: Populate a "Client" object accessible by the Service
}
});
My question is, how can I now provide the service in question with the "Client" object that I create based on the value in the magic header?
From the looks of it, my only option is passing this information in via the DTO. So I thought about adding a base class that all my DTOs inherit from, and this base class would contain a Client property.
Is that the correct approach?
The way to pass any information that's available in all Filters and Service in the same request is to use the httpReq.Items object Dictionary, e.g. Dictionary<string,object>"
RequestFilters.Add((httpReq, httpResp, requestDto) =>
{
if(httpReq.Headers["MyMagicHeader"] != "magic")
{
throw HttpError.Unauthorized("Unauthorized");
}
else
{
//TODO: Populate a "Client" object accessible by the Service
httpReq.Items["MagicToken"] = CreateMagicValue(httpReq.Headers["MyMagicHeader"]);
}
});

Resources